Skip to content

Commit

Permalink
Merge pull request #1839 from greatSumini/feat/allow-enums-for-orphan…
Browse files Browse the repository at this point in the history
…ed-types

feat(schema-builder): allow enums for `orphanedTypes`
  • Loading branch information
kamilmysliwiec committed Nov 24, 2021
2 parents 696826d + bfb28fb commit f2015f5
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 19 deletions.
4 changes: 2 additions & 2 deletions lib/interfaces/build-schema-options.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ export interface BuildSchemaOptions {
scalarsMap?: ScalarsTypeMap[];

/**
* Orphaned type classes that are not explicitly used in GraphQL types definitions
* Orphaned type classes/enums that are not explicitly used in GraphQL types definitions
*/
orphanedTypes?: Function[];
orphanedTypes?: (Function | object)[];

/**
* Disable checking on build the correctness of a schema
Expand Down
29 changes: 19 additions & 10 deletions lib/schema-builder/factories/orphaned-types.factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class OrphanedTypesFactory {
private readonly orphanedReferenceRegistry: OrphanedReferenceRegistry,
) {}

public create(types: Function[]): GraphQLNamedType[] {
public create(types: (Function | object)[]): GraphQLNamedType[] {
types = (types || []).concat(this.orphanedReferenceRegistry.getAll());

if (types.length === 0) {
Expand All @@ -26,14 +26,23 @@ export class OrphanedTypesFactory {
...objectTypeDefs,
...inputTypeDefs,
];
return classTypeDefs
.filter((item) => !item.isAbstract)
.filter((item: ObjectTypeDefinition) => {
const implementsReferencedInterface = getInterfacesArray(
item.interfaces,
).some((i) => types.includes(i));
return types.includes(item.target) || implementsReferencedInterface;
})
.map(({ type }) => type);

const enumTypeDefs =
this.typeDefinitionsStorage.getAllEnumTypeDefinitions();

return [
...classTypeDefs
.filter((item) => !item.isAbstract)
.filter((item: ObjectTypeDefinition) => {
const implementsReferencedInterface = getInterfacesArray(
item.interfaces,
).some((i) => types.includes(i));
return types.includes(item.target) || implementsReferencedInterface;
})
.map(({ type }) => type),
...enumTypeDefs
.filter((item) => types.includes(item.enumRef))
.map(({ type }) => type),
];
}
}
4 changes: 4 additions & 0 deletions lib/schema-builder/storages/type-definitions.storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export class TypeDefinitionsStorage {
return this.enumTypeDefinitions.get(obj);
}

getAllEnumTypeDefinitions(): EnumDefinition[] {
return Array.from(this.enumTypeDefinitions.values());
}

addUnions(unionDefs: UnionDefinition[]) {
unionDefs.forEach(item => this.unionTypeDefinitions.set(item.id, item));
}
Expand Down
5 changes: 2 additions & 3 deletions lib/schema-builder/storages/type-metadata.storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export class TypeMetadataStorageHost {
this.params.push(metadata);
}

compile(orphanedTypes: Function[] = []) {
compile(orphanedTypes: (Function | object)[] = []) {
this.classDirectives.reverse();
this.classExtensions.reverse();
this.fieldDirectives.reverse();
Expand All @@ -237,8 +237,7 @@ export class TypeMetadataStorageHost {
this.compileExtendedResolversMetadata();

orphanedTypes
.filter((type) => type && type.prototype)
.forEach((type) => this.applyPluginMetadata(type.prototype));
.forEach((type) => 'prototype' in type && this.applyPluginMetadata(type.prototype));
}

loadClassPluginMetadata(metadata: ClassMetadata[]) {
Expand Down
13 changes: 13 additions & 0 deletions tests/code-first/enums/sample-orphaned.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { registerEnumType } from '../../../lib';

export enum SampleOrphanedEnum {
Red = 'RED',
Blue = 'BLUE',
Black = 'BLACK',
White = 'WHITE',
}

registerEnumType(SampleOrphanedEnum, {
name: 'SampleOrphanedEnum',
description: 'orphaned enum',
});
50 changes: 46 additions & 4 deletions tests/e2e/code-first-schema.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import {
IntrospectionField,
IntrospectionSchema,
printSchema,
TypeKind
TypeKind,
} from 'graphql';
import { GRAPHQL_SDL_FILE_HEADER } from '../../lib/graphql.constants';
import {
GraphQLSchemaBuilderModule,
GraphQLSchemaFactory
GraphQLSchemaFactory,
} from '../../lib/schema-builder';
import { DirectionsResolver } from '../code-first/directions/directions.resolver';
import { AbstractResolver } from '../code-first/other/abstract.resolver';
import { SampleOrphanedEnum } from '../code-first/enums/sample-orphaned.enum';
import { SampleOrphanedType } from '../code-first/other/sample-orphaned.type';
import { IRecipesResolver } from '../code-first/recipes/irecipes.resolver';
import { Recipe } from '../code-first/recipes/models/recipe';
Expand All @@ -25,7 +26,7 @@ import {
getQuery,
getQueryByName,
getSubscription,
getSubscriptionByName
getSubscriptionByName,
} from '../utils/introspection-schema.utils';
import { printedSchemaSnapshot } from '../utils/printed-schema.snapshot';

Expand All @@ -52,8 +53,9 @@ describe('Code-first - schema factory', () => {
AbstractResolver,
IRecipesResolver,
],
{ orphanedTypes: [SampleOrphanedType] },
{ orphanedTypes: [SampleOrphanedType, SampleOrphanedEnum] },
);

introspectionSchema = await (
await graphql(schema, getIntrospectionQuery())
).data.__schema;
Expand Down Expand Up @@ -487,6 +489,46 @@ describe('Code-first - schema factory', () => {
}),
);
});

it('should define "SampleOrphanedEnum" orphaned type', () => {
const type = introspectionSchema.types.find(
({ name }) => name === 'SampleOrphanedEnum',
);
expect(type).toEqual(
expect.objectContaining({
kind: TypeKind.ENUM,
name: 'SampleOrphanedEnum',
description: 'orphaned enum',
enumValues: [
{
deprecationReason: null,
description: null,
isDeprecated: false,
name: 'Red',
},
{
deprecationReason: null,
description: null,
isDeprecated: false,
name: 'Blue',
},
{
deprecationReason: null,
description: null,
isDeprecated: false,
name: 'Black',
},
{
deprecationReason: null,
description: null,
isDeprecated: false,
name: 'White',
},
],
}),
);
});

it('should define "IRecipe" interface', () => {
const type = introspectionSchema.types.find(
({ name }) => name === 'IRecipe',
Expand Down
8 changes: 8 additions & 0 deletions tests/utils/printed-schema.snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ type Ingredient {
name: String @deprecated(reason: "is deprecated")
}
"""orphaned enum"""
enum SampleOrphanedEnum {
Red
Blue
Black
White
}
type Query {
"""get recipe by id"""
recipe(
Expand Down

0 comments on commit f2015f5

Please sign in to comment.