diff --git a/adonis-typings/server.ts b/adonis-typings/server.ts index f0d2bf2..e0e5acd 100644 --- a/adonis-typings/server.ts +++ b/adonis-typings/server.ts @@ -16,6 +16,11 @@ declare module '@ioc:Zakodium/Apollo/Server' { export type Upload = Promise; + export interface ApolloExceptionFormatter { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + formatError: Exclude['formatError'], undefined>; + } + class ApolloServer { public applyMiddleware(): void; public getGraphqlHandler(): RouteHandler; @@ -102,4 +107,17 @@ declare module '@ioc:Zakodium/Apollo/Server' { 'typeDefs' | 'resolvers' >; } + + type ApolloServerUserOptions = Omit< + ApolloServerOptions, + 'formatError' + > & { + formatError?: ApolloServerOptions['formatError'] | string; + }; + export type ApolloUserConfig = Omit< + ApolloConfig, + 'apolloServer' + > & { + apolloServer: ApolloServerUserOptions; + }; } diff --git a/providers/ApolloProvider.ts b/providers/ApolloProvider.ts index 026c7a5..e0120f9 100644 --- a/providers/ApolloProvider.ts +++ b/providers/ApolloProvider.ts @@ -1,7 +1,7 @@ import { ApplicationContract } from '@ioc:Adonis/Core/Application'; -import { ApolloConfig } from '@ioc:Zakodium/Apollo/Server'; import ApolloServer from '../src/ApolloServer'; +import createApolloConfig from '../src/createApolloConfig'; export default class ApolloProvider { protected loading = false; @@ -15,16 +15,16 @@ export default class ApolloProvider { 'ApolloProvider was called during its initialization. To use this provider in resolvers, use dynamic `import()`.', ); } - let apolloConfig = this.app.config.get('apollo', {}) as ApolloConfig; - const appUrl = this.app.env.get('APP_URL') as string; - if (!apolloConfig.appUrl && appUrl) { - apolloConfig = { - ...apolloConfig, - appUrl, - }; - } + const apolloConfig = createApolloConfig( + this.app.config.get('apollo', {}), + { + ioc: this.app.container, + fallbackUrl: this.app.env.get('APP_URL'), + }, + ); this.loading = true; + return new ApolloServer(this.app, apolloConfig, this.app.logger); }); } diff --git a/src/__tests__/createApolloConfig.test.ts b/src/__tests__/createApolloConfig.test.ts new file mode 100644 index 0000000..40bb418 --- /dev/null +++ b/src/__tests__/createApolloConfig.test.ts @@ -0,0 +1,76 @@ +import { Ioc } from '@adonisjs/fold'; + +import { ApolloExceptionFormatter } from '@ioc:Zakodium/Apollo/Server'; + +import createApolloConfig from '../createApolloConfig'; + +const testIoC = new Ioc(); + +class Formatter implements ApolloExceptionFormatter { + formatError() { + return { + message: 'error', + }; + } +} +testIoC.singleton('App/Exceptions/GraphqlHandler', () => new Formatter()); + +describe('apollo config', () => { + it('create apollo configuration, formatError is callback', () => { + const config = createApolloConfig( + { + schemas: 'app/Schemas', + resolvers: 'app/Resolvers', + path: '/graphql', + apolloServer: { + introspection: true, + formatError: () => ({ message: 'error' }), + }, + }, + { + ioc: testIoC, + fallbackUrl: 'http://localhost:3333', + }, + ); + expect(config).toEqual({ + schemas: 'app/Schemas', + resolvers: 'app/Resolvers', + path: '/graphql', + apolloServer: { + introspection: true, + formatError: expect.any(Function), + }, + }); + }); + + it('create apollo configuration, formatError is an IoC dependency', () => { + const config = createApolloConfig( + { + schemas: 'app/Schemas', + resolvers: 'app/Resolvers', + path: '/graphql', + apolloServer: { + introspection: true, + formatError: 'App/Exceptions/GraphqlHandler', + }, + }, + { + ioc: testIoC, + fallbackUrl: 'http://localhost:3333', + }, + ); + expect(config).toEqual({ + schemas: 'app/Schemas', + resolvers: 'app/Resolvers', + path: '/graphql', + apolloServer: { + introspection: true, + formatError: expect.any(Function), + }, + }); + // @ts-expect-error We don't care about the arguments here. + expect(config.apolloServer?.formatError()).toEqual({ + message: 'error', + }); + }); +}); diff --git a/src/createApolloConfig.ts b/src/createApolloConfig.ts new file mode 100644 index 0000000..9c27dce --- /dev/null +++ b/src/createApolloConfig.ts @@ -0,0 +1,35 @@ +import { IocContract } from '@adonisjs/fold'; +import { ApolloServerOptions, BaseContext } from '@apollo/server'; + +import type { + ApolloConfig, + ApolloUserConfig, +} from '@ioc:Zakodium/Apollo/Server'; + +export default function createApolloConfig( + config: ApolloUserConfig, + options: { + ioc: IocContract; + fallbackUrl?: string; + }, +): ApolloConfig { + return { + ...config, + apolloServer: { + ...config.apolloServer, + formatError: + typeof config.apolloServer?.formatError === 'string' + ? makeFormatter(options.ioc, config.apolloServer.formatError) + : config.apolloServer.formatError, + }, + }; +} + +function makeFormatter( + ioc: IocContract, + formatterPath: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): ApolloServerOptions['formatError'] { + const formatter = ioc.make(formatterPath); + return formatter.formatError.bind(formatter); +}