diff --git a/packages/apollo/tests/code-first-extensions/app.module.ts b/packages/apollo/tests/code-first-extensions/app.module.ts new file mode 100644 index 000000000..cfb4cd2e0 --- /dev/null +++ b/packages/apollo/tests/code-first-extensions/app.module.ts @@ -0,0 +1,19 @@ +import { GraphQLModule } from '@nestjs/graphql'; +import { ApolloDriver, ApolloDriverConfig } from '../../lib'; +import { UserModule } from './user/user.module'; +import { Module } from '@nestjs/common'; + +/** + * Main application module for the code-first extensions example. + */ +@Module({ + imports: [ + GraphQLModule.forRoot({ + driver: ApolloDriver, + autoSchemaFile: true, + playground: false, + }), + UserModule, + ], +}) +export class AppModule {} diff --git a/packages/apollo/tests/code-first-extensions/user/create-user.input.ts b/packages/apollo/tests/code-first-extensions/user/create-user.input.ts new file mode 100644 index 000000000..0d934e59d --- /dev/null +++ b/packages/apollo/tests/code-first-extensions/user/create-user.input.ts @@ -0,0 +1,9 @@ +import { Extensions, Field, InputType } from '@nestjs/graphql'; + +@InputType() +@Extensions({ exampleExtension: 'exampleValue' }) +export class CreateUserInput { + @Extensions({ fieldLevelExtension: 123 }) + @Field() + name!: string; +} diff --git a/packages/apollo/tests/code-first-extensions/user/user-status.dto.ts b/packages/apollo/tests/code-first-extensions/user/user-status.dto.ts new file mode 100644 index 000000000..7ea2de94b --- /dev/null +++ b/packages/apollo/tests/code-first-extensions/user/user-status.dto.ts @@ -0,0 +1,11 @@ +import { ID, Field, ObjectType, Extensions } from '@nestjs/graphql'; + +@ObjectType() +export class Status { + @Field(() => ID) + id!: string; + + @Field() + @Extensions({ isPublic: true }) + code!: string; +} diff --git a/packages/apollo/tests/code-first-extensions/user/user.dto.ts b/packages/apollo/tests/code-first-extensions/user/user.dto.ts new file mode 100644 index 000000000..95aa4a919 --- /dev/null +++ b/packages/apollo/tests/code-first-extensions/user/user.dto.ts @@ -0,0 +1,15 @@ +import { Extensions, Field, ID, ObjectType } from '@nestjs/graphql'; +import { Status } from './user-status.dto'; + +@ObjectType() +export class User { + @Field(() => ID) + id!: string; + + @Field() + name!: string; + + @Extensions({ isPublic: true }) + @Field(() => Status, { nullable: true, description: 'DTO Description' }) + status?: Status; +} diff --git a/packages/apollo/tests/code-first-extensions/user/user.module.ts b/packages/apollo/tests/code-first-extensions/user/user.module.ts new file mode 100644 index 000000000..33ea94408 --- /dev/null +++ b/packages/apollo/tests/code-first-extensions/user/user.module.ts @@ -0,0 +1,8 @@ +import { Module } from '@nestjs/common'; +import { UserResolver } from './user.resolver'; +import { UserService } from './user.service'; + +@Module({ + providers: [UserResolver, UserService], +}) +export class UserModule {} diff --git a/packages/apollo/tests/code-first-extensions/user/user.resolver.ts b/packages/apollo/tests/code-first-extensions/user/user.resolver.ts new file mode 100644 index 000000000..eb01fcdb6 --- /dev/null +++ b/packages/apollo/tests/code-first-extensions/user/user.resolver.ts @@ -0,0 +1,38 @@ +import { + Args, + Mutation, + Parent, + Query, + ResolveField, + Resolver, +} from '@nestjs/graphql'; +import { User } from './user.dto'; +import { UserService } from './user.service'; +import { CreateUserInput } from './create-user.input'; +import { Status } from './user-status.dto'; + +@Resolver(() => User) +export class UserResolver { + constructor(private readonly userService: UserService) {} + + @Query(() => [User], { name: 'users' }) + findAll(): User[] { + return this.userService.findAll(); + } + + @Mutation(() => User) + createUser(@Args('createUserInput') createUserInput: CreateUserInput): User { + return this.userService.create(createUserInput); + } + + @ResolveField('status', undefined, { + nullable: true, + description: 'Resolve Field Description', + }) + getStatus(@Parent() user: User): Status { + return { + id: 'status-id', + code: 'ACTIVE', + }; + } +} diff --git a/packages/apollo/tests/code-first-extensions/user/user.service.ts b/packages/apollo/tests/code-first-extensions/user/user.service.ts new file mode 100644 index 000000000..44526f4c5 --- /dev/null +++ b/packages/apollo/tests/code-first-extensions/user/user.service.ts @@ -0,0 +1,26 @@ +import { Injectable } from '@nestjs/common'; +import { User } from './user.dto'; +import { CreateUserInput } from './create-user.input'; + +@Injectable() +export class UserService { + private users: User[] = []; + private idCounter = 1; + + findAll(): User[] { + return this.users; + } + + findOne(id: string): User | undefined { + return this.users.find((user) => user.id === id); + } + + create(createUserInput: CreateUserInput): User { + const user: User = { + id: String(this.idCounter++), + name: createUserInput.name, + }; + this.users.push(user); + return user; + } +} diff --git a/packages/apollo/tests/e2e/code-first-extensions.spec.ts b/packages/apollo/tests/e2e/code-first-extensions.spec.ts new file mode 100644 index 000000000..d745328e8 --- /dev/null +++ b/packages/apollo/tests/e2e/code-first-extensions.spec.ts @@ -0,0 +1,35 @@ +import { INestApplication } from '@nestjs/common'; +import { GraphQLSchemaHost } from '@nestjs/graphql'; +import { Test } from '@nestjs/testing'; +import * as assert from 'assert'; +import { GraphQLObjectType } from 'graphql'; +import { AppModule } from '../code-first-extensions/app.module'; + +describe('Code-first extensions', () => { + let app: INestApplication; + + beforeAll(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleRef.createNestApplication(); + await app.init(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('Adds extensions to resolved field', async () => { + const { schema } = app.get(GraphQLSchemaHost); + const userObject = schema.getType('User') as GraphQLObjectType; + assert(userObject, 'User type not found in schema'); + const statusField = userObject.getFields().status; + assert(statusField, 'status field not found in User type'); + + const extensions = statusField.extensions; + + expect(extensions.isPublic).toBe(true); + }); +}); diff --git a/packages/graphql/lib/schema-builder/storages/type-metadata.storage.ts b/packages/graphql/lib/schema-builder/storages/type-metadata.storage.ts index 4af81b1d3..eec797529 100644 --- a/packages/graphql/lib/schema-builder/storages/type-metadata.storage.ts +++ b/packages/graphql/lib/schema-builder/storages/type-metadata.storage.ts @@ -421,7 +421,7 @@ export class TypeMetadataStorageHost { } let objectOrInterfaceTypeField = objectOrInterfaceTypeMetadata.properties.find( - (fieldDef) => fieldDef.name === item.methodName, + (fieldDef) => fieldDef.schemaName === item.schemaName, ); for ( let _objectTypeRef = objectTypeRef; @@ -430,7 +430,7 @@ export class TypeMetadataStorageHost { ) { const possibleTypeMetadata = getTypeMetadata(_objectTypeRef); objectOrInterfaceTypeField = possibleTypeMetadata?.properties.find( - (fieldDef) => fieldDef.name === item.methodName, + (fieldDef) => fieldDef.schemaName === item.schemaName, ); if (objectOrInterfaceTypeField) { objectTypeRef = _objectTypeRef; diff --git a/packages/graphql/tests/graphql.factory.spec.ts b/packages/graphql/tests/graphql.factory.spec.ts index 37f62c1f9..c777c7ec5 100644 --- a/packages/graphql/tests/graphql.factory.spec.ts +++ b/packages/graphql/tests/graphql.factory.spec.ts @@ -4,7 +4,6 @@ import { GraphQLAstExplorer, GraphQLFactory } from '../lib'; import { GraphQLSchemaBuilder } from '../lib/graphql-schema.builder'; import { ResolversExplorerService } from '../lib/services/resolvers-explorer.service'; import { ScalarsExplorerService } from '../lib/services/scalars-explorer.service'; -import gql from 'graphql-tag'; import { GraphQLSchema } from 'graphql'; describe('GraphQLFactory', () => {