Skip to content

Commit

Permalink
feat: provide another way of creating the formatError handler which e…
Browse files Browse the repository at this point in the history
…nables importing IoC dependencies (#56)

defines and exports an interface to be implemented by the error formatter
  • Loading branch information
stropitek committed Jan 9, 2024
1 parent 4a45139 commit ee27363
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 9 deletions.
18 changes: 18 additions & 0 deletions adonis-typings/server.ts
Expand Up @@ -16,6 +16,11 @@ declare module '@ioc:Zakodium/Apollo/Server' {

export type Upload = Promise<FileUpload>;

export interface ApolloExceptionFormatter {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
formatError: Exclude<ApolloServerOptions<any>['formatError'], undefined>;
}

class ApolloServer {
public applyMiddleware(): void;
public getGraphqlHandler(): RouteHandler;
Expand Down Expand Up @@ -102,4 +107,17 @@ declare module '@ioc:Zakodium/Apollo/Server' {
'typeDefs' | 'resolvers'
>;
}

type ApolloServerUserOptions<ContextType extends BaseContext> = Omit<
ApolloServerOptions<ContextType>,
'formatError'
> & {
formatError?: ApolloServerOptions<ContextType>['formatError'] | string;
};
export type ApolloUserConfig<ContextType extends BaseContext> = Omit<
ApolloConfig<ContextType>,
'apolloServer'
> & {
apolloServer: ApolloServerUserOptions<ContextType>;
};
}
18 changes: 9 additions & 9 deletions 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;
Expand All @@ -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);
});
}
Expand Down
76 changes: 76 additions & 0 deletions 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',
});
});
});
35 changes: 35 additions & 0 deletions 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<ContextType extends BaseContext>(
config: ApolloUserConfig<ContextType>,
options: {
ioc: IocContract;
fallbackUrl?: string;
},
): ApolloConfig<ContextType> {
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<any>['formatError'] {
const formatter = ioc.make(formatterPath);
return formatter.formatError.bind(formatter);
}

0 comments on commit ee27363

Please sign in to comment.