Skip to content

Commit

Permalink
Merge pull request #2546 from tugascript/tugascript/add-mercurius-hoo…
Browse files Browse the repository at this point in the history
…ks-object

feat(mercurius): add mercurius hooks
  • Loading branch information
kamilmysliwiec committed Feb 7, 2023
2 parents cd65d44 + 689c77a commit 50dde67
Show file tree
Hide file tree
Showing 21 changed files with 717 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { IncomingMessage, Server, ServerResponse } from 'http';
import mercurius from 'mercurius';
import { MercuriusDriverConfig } from '../interfaces/mercurius-driver-config.interface';
import { buildMercuriusFederatedSchema } from '../utils/build-mercurius-federated-schema.util';
import { registerMercuriusHooks } from '../utils/register-mercurius-hooks.util';
import { registerMercuriusPlugin } from '../utils/register-mercurius-plugin.util';

@Injectable()
Expand All @@ -29,7 +30,7 @@ export class MercuriusFederationDriver extends AbstractGraphQLDriver<MercuriusDr
}

public async start(options: MercuriusDriverConfig) {
const { plugins, ...adapterOptions } =
const { plugins, hooks, ...adapterOptions } =
await this.graphqlFederationFactory.mergeWithSchema(
options,
buildMercuriusFederatedSchema,
Expand All @@ -53,6 +54,7 @@ export class MercuriusFederationDriver extends AbstractGraphQLDriver<MercuriusDr
...adapterOptions,
});
await registerMercuriusPlugin(app, plugins);
await registerMercuriusHooks(app, hooks);
}

/* eslit-disable-next-line @typescript-eslint/no-empty-function */
Expand Down
4 changes: 3 additions & 1 deletion packages/mercurius/lib/drivers/mercurius-gateway.driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FastifyInstance, FastifyLoggerInstance } from 'fastify';
import { IncomingMessage, Server, ServerResponse } from 'http';
import mercurius from 'mercurius';
import { MercuriusDriverConfig } from '../interfaces/mercurius-driver-config.interface';
import { registerMercuriusHooks } from '../utils/register-mercurius-hooks.util';
import { registerMercuriusPlugin } from '../utils/register-mercurius-plugin.util';

export class MercuriusGatewayDriver extends AbstractGraphQLDriver<MercuriusDriverConfig> {
Expand All @@ -23,12 +24,13 @@ export class MercuriusGatewayDriver extends AbstractGraphQLDriver<MercuriusDrive
throw new Error(`No support for current HttpAdapter: ${platformName}`);
}

const { plugins, ...mercuriusOptions } = options;
const { plugins, hooks, ...mercuriusOptions } = options;
const app = httpAdapter.getInstance<FastifyInstance>();
await app.register(mercurius, {
...mercuriusOptions,
});
await registerMercuriusPlugin(app, plugins);
await registerMercuriusHooks(app, hooks);
}

public async stop(): Promise<void> {}
Expand Down
4 changes: 3 additions & 1 deletion packages/mercurius/lib/drivers/mercurius.driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { printSchema } from 'graphql';
import { IncomingMessage, Server, ServerResponse } from 'http';
import mercurius from 'mercurius';
import { MercuriusDriverConfig } from '../interfaces/mercurius-driver-config.interface';
import { registerMercuriusHooks } from '../utils/register-mercurius-hooks.util';
import { registerMercuriusPlugin } from '../utils/register-mercurius-plugin.util';

export class MercuriusDriver extends AbstractGraphQLDriver<MercuriusDriverConfig> {
Expand All @@ -18,7 +19,7 @@ export class MercuriusDriver extends AbstractGraphQLDriver<MercuriusDriverConfig
}

public async start(mercuriusOptions: MercuriusDriverConfig) {
const { plugins, ...options } =
const { plugins, hooks, ...options } =
await this.graphQlFactory.mergeWithSchema<MercuriusDriverConfig>(
mercuriusOptions,
);
Expand All @@ -41,6 +42,7 @@ export class MercuriusDriver extends AbstractGraphQLDriver<MercuriusDriverConfig
...options,
});
await registerMercuriusPlugin(app, plugins);
await registerMercuriusHooks(app, hooks);
}

public async stop(): Promise<void> {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import {
GqlOptionsFactory,
} from '@nestjs/graphql';
import { MercuriusOptions } from 'mercurius';
import { MercuriusHooks } from './mercurius-hook.interface';
import { MercuriusPlugins } from './mercurius-plugin.interface';

export type MercuriusDriverConfig = GqlModuleOptions &
MercuriusOptions &
MercuriusPlugins;
MercuriusPlugins &
MercuriusHooks;

export type MercuriusDriverConfigFactory =
GqlOptionsFactory<MercuriusDriverConfig>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import {
GqlOptionsFactory,
} from '@nestjs/graphql';
import { MercuriusCommonOptions, MercuriusGatewayOptions } from 'mercurius';
import { MercuriusHooks } from './mercurius-hook.interface';
import { MercuriusPlugin } from './mercurius-plugin.interface';

export type MercuriusGatewayDriverConfig = GqlModuleOptions &
MercuriusCommonOptions &
MercuriusGatewayOptions &
MercuriusPlugin;
MercuriusPlugin &
MercuriusHooks;

export type MercuriusGatewayDriverConfigFactory =
GqlOptionsFactory<MercuriusGatewayDriverConfig>;
Expand Down
69 changes: 69 additions & 0 deletions packages/mercurius/lib/interfaces/mercurius-hook.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import {
MercuriusContext,
onGatewayReplaceSchemaHookHandler,
onResolutionHookHandler,
onSubscriptionEndHookHandler,
onSubscriptionResolutionHookHandler,
preExecutionHookHandler,
preGatewayExecutionHookHandler,
preGatewaySubscriptionExecutionHookHandler,
preParsingHookHandler,
preSubscriptionExecutionHookHandler,
preSubscriptionParsingHookHandler,
preValidationHookHandler,
} from 'mercurius';

export interface MercuriusHooksObject<
Context extends MercuriusContext = MercuriusContext,
> {
preParsing?:
| preParsingHookHandler<Context>
| preParsingHookHandler<Context>[];
preValidation?:
| preValidationHookHandler<Context>
| preValidationHookHandler<Context>[];
preExecution?:
| preExecutionHookHandler<Context>
| preExecutionHookHandler<Context>[];
onResolution?:
| onResolutionHookHandler<Context>
| onResolutionHookHandler<Context>[];
preSubscriptionParsing?:
| preSubscriptionParsingHookHandler<Context>
| preSubscriptionParsingHookHandler<Context>[];
preSubscriptionExecution?:
| preSubscriptionExecutionHookHandler<Context>
| preSubscriptionExecutionHookHandler<Context>[];
onSubscriptionResolution?:
| onSubscriptionResolutionHookHandler<Context>
| onSubscriptionResolutionHookHandler<Context>[];
onSubscriptionEnd?:
| onSubscriptionEndHookHandler<Context>
| onSubscriptionEndHookHandler<Context>[];
}

export interface MercuriusHooks<
Context extends MercuriusContext = MercuriusContext,
> {
hooks?: MercuriusHooksObject<Context>;
}

export interface MercuriusGatewayHooksObject<
Context extends MercuriusContext = MercuriusContext,
> extends MercuriusHooksObject<Context> {
preGatewayExecution?:
| preGatewayExecutionHookHandler<Context>
| preGatewayExecutionHookHandler<Context>[];
preGatewaySubscriptionExecution?:
| preGatewaySubscriptionExecutionHookHandler<Context>
| preGatewaySubscriptionExecutionHookHandler<Context>[];
onGatewayReplaceSchema?:
| onGatewayReplaceSchemaHookHandler
| onGatewayReplaceSchemaHookHandler[];
}

export interface MercuriusGatewayHooks<
Context extends MercuriusContext = MercuriusContext,
> {
hooks?: MercuriusGatewayHooksObject<Context>;
}
25 changes: 25 additions & 0 deletions packages/mercurius/lib/utils/register-mercurius-hooks.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { FastifyInstance } from 'fastify';
import { MercuriusGatewayHooksObject } from '../interfaces/mercurius-hook.interface';
import { isArray, isNull, isUndefined } from './validation.util';

export function registerMercuriusHooks(
app: FastifyInstance,
hooks?: MercuriusGatewayHooksObject | null,
): void {
if (isUndefined(hooks) || isNull(hooks)) {
return;
}

Object.entries(hooks).forEach(([hookName, hookFn]: [any, any]) => {
if (isUndefined(hookFn) || isNull(hookFn)) {
return;
}

if (isArray<any>(hookFn)) {
hookFn.forEach((fn) => app.graphql.addHook(hookName, fn));
return;
}

app.graphql.addHook(hookName, hookFn);
});
}
80 changes: 80 additions & 0 deletions packages/mercurius/tests/e2e/base-hooks-array.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { INestApplication } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter } from '@nestjs/platform-fastify';
import * as request from 'supertest';
import { ApplicationModule } from '../hooks/base-array/hooks.module';
import { MockLogger } from '../hooks/mocks/logger.mock';

describe('Base hooks in array format', () => {
let app: INestApplication;
let logger: MockLogger;

beforeEach(async () => {
logger = new MockLogger();
app = await NestFactory.create(ApplicationModule, new FastifyAdapter(), {
logger,
});
await app.init();
await app.getHttpAdapter().getInstance().ready();
});

it('hooks should be triggered', async () => {
await request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: '{ getAnimalName }',
})
.expect(200, {
data: {
getAnimalName: 'cat',
},
});
expect(logger.warn).toHaveBeenCalledTimes(8);
expect(logger.warn).toHaveBeenNthCalledWith(
1,
'preParsing1',
'GqlConfigService',
);
expect(logger.warn).toHaveBeenNthCalledWith(
2,
'preParsing2',
'GqlConfigService',
);
expect(logger.warn).toHaveBeenNthCalledWith(
3,
'preValidation1',
'GqlConfigService',
);
expect(logger.warn).toHaveBeenNthCalledWith(
4,
'preValidation2',
'GqlConfigService',
);
expect(logger.warn).toHaveBeenNthCalledWith(
5,
'preExecution1',
'GqlConfigService',
);
expect(logger.warn).toHaveBeenNthCalledWith(
6,
'preExecution2',
'GqlConfigService',
);
expect(logger.warn).toHaveBeenNthCalledWith(
7,
'onResolution1',
'GqlConfigService',
);
expect(logger.warn).toHaveBeenNthCalledWith(
8,
'onResolution2',
'GqlConfigService',
);
});

afterEach(async () => {
await app.close();
});
});
60 changes: 60 additions & 0 deletions packages/mercurius/tests/e2e/base-hooks.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { INestApplication } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter } from '@nestjs/platform-fastify';
import * as request from 'supertest';
import { ApplicationModule } from '../hooks/base/hooks.module';
import { MockLogger } from '../hooks/mocks/logger.mock';

describe('Base hooks', () => {
let app: INestApplication;
let logger: MockLogger;

beforeEach(async () => {
logger = new MockLogger();
app = await NestFactory.create(ApplicationModule, new FastifyAdapter(), {
logger,
});
await app.init();
await app.getHttpAdapter().getInstance().ready();
});

it('hooks should be triggered', async () => {
await request(app.getHttpServer())
.post('/graphql')
.send({
operationName: null,
variables: {},
query: '{ getAnimalName }',
})
.expect(200, {
data: {
getAnimalName: 'cat',
},
});
expect(logger.warn).toHaveBeenCalledTimes(4);
expect(logger.warn).toHaveBeenNthCalledWith(
1,
'preParsing',
'GqlConfigService',
);
expect(logger.warn).toHaveBeenNthCalledWith(
2,
'preValidation',
'GqlConfigService',
);
expect(logger.warn).toHaveBeenNthCalledWith(
3,
'preExecution',
'GqlConfigService',
);
expect(logger.warn).toHaveBeenNthCalledWith(
4,
'onResolution',
'GqlConfigService',
);
});

afterEach(async () => {
await app.close();
});
});

0 comments on commit 50dde67

Please sign in to comment.