diff --git a/packages/handler-graphql/__tests__/disableIntrospectionQuery.test.ts b/packages/handler-graphql/__tests__/disableIntrospectionQuery.test.ts deleted file mode 100644 index 6e575d33466..00000000000 --- a/packages/handler-graphql/__tests__/disableIntrospectionQuery.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { ContextPlugin } from "@webiny/api"; -import useGqlHandler from "./useGqlHandler"; - -import { booksCrudPlugin, booksSchema } from "~tests/mocks/booksSchema.legacy.js"; - -const disableIntrospectionPlugin = new ContextPlugin(async context => { - // Check in the context.request.body if there is an introspection query. - const body = context.request.body as unknown as Record; - if (!body?.query) { - return; - } else if ((body.query as string).includes("__schema") === false) { - return; - } - - // Then you can end the request with reply hijacking. - context.reply - .send({ - message: "Forbidden to execute introspection queries." - }) - .status(403) - .hijack(); -}); -describe("disable introspection query", () => { - // TODO: context.reply.hijack() is a legacy Fastify/Lambda pattern not available in the - // DI-native handler. Disabling introspection now requires a different mechanism. - it.skip("should not allow to run introspection query", async () => { - const { introspect: enabledIntrospect } = useGqlHandler({ - plugins: [booksSchema] - }); - - const [enabledIntrospectResponse] = await enabledIntrospect(); - - expect(enabledIntrospectResponse).toMatchObject({ - data: { - __schema: expect.any(Object) - } - }); - - const { introspect: disabledIntrospect, invoke: disabledIntrospectInvoke } = useGqlHandler({ - plugins: [booksCrudPlugin, booksSchema, disableIntrospectionPlugin] - }); - - const [response, rawResponse] = await disabledIntrospect(); - - expect(rawResponse).toMatchObject({ - statusCode: 403 - }); - - expect(response).toEqual({ - message: "Forbidden to execute introspection queries." - }); - - const [booksResponse] = await disabledIntrospectInvoke({ - body: { query: `{ books { name } }` } - }); - expect(booksResponse.errors).toBeFalsy(); - expect(booksResponse.data.books.length).toBe(2); - }); -}); diff --git a/packages/handler-graphql/__tests__/graphql.test.ts b/packages/handler-graphql/__tests__/graphql.test.ts index 9a07b4e13a7..506e1b5b3ac 100644 --- a/packages/handler-graphql/__tests__/graphql.test.ts +++ b/packages/handler-graphql/__tests__/graphql.test.ts @@ -3,8 +3,7 @@ import useGqlHandler from "./useGqlHandler"; import { booksSchemaPlugin, booksCrudPlugin } from "~tests/mocks/booksSchema"; import { CoreGraphQLSchemaFactory } from "~/graphql/abstractions"; import type { GraphQLSchemaBuilder } from "~/features/GraphQLSchemaBuilder/abstractions"; -import { createContextPlugin } from "@webiny/handler"; -import type { Context } from "./types"; +import type { Container } from "@webiny/di"; describe("GraphQL Handler", () => { test("should return errors if schema doesn't exist", async () => { @@ -62,9 +61,9 @@ describe("GraphQL Handler", () => { dependencies: [] }); - const decoratorsPlugin = createContextPlugin(context => { - context.container.register(DecoratorsSchemaImpl); - }); + const decoratorsPlugin = (container: Container) => { + container.register(DecoratorsSchemaImpl); + }; const { invoke } = useGqlHandler({ plugins: [booksCrudPlugin, booksSchemaPlugin, decoratorsPlugin] diff --git a/packages/handler-graphql/__tests__/mocks/booksSchema.legacy.ts b/packages/handler-graphql/__tests__/mocks/booksSchema.legacy.ts index 16113082052..07185246829 100644 --- a/packages/handler-graphql/__tests__/mocks/booksSchema.legacy.ts +++ b/packages/handler-graphql/__tests__/mocks/booksSchema.legacy.ts @@ -1,4 +1,5 @@ -import { createContextPlugin } from "@webiny/handler"; +import type { Container } from "@webiny/di"; +import { RequestContextInitializer } from "@webiny/event-handler-core"; import type { Book, Context } from "~tests/types"; import { createGraphQLSchemaPlugin } from "~/plugins"; @@ -11,14 +12,19 @@ export const books: Book[] = [ } ]; -export const booksCrudPlugin = createContextPlugin(async context => { - context.getBooks = async () => { - console.log("getBooks"); - console.table(books); - console.warn("Your store is quite empty!"); - return books; - }; -}); +// Augments the request context with `getBooks` post-auth (the Query.books resolver reads it). +export const booksCrudPlugin = (container: Container) => { + container.registerInstance(RequestContextInitializer, { + async init(context: Record) { + context.getBooks = async () => { + console.log("getBooks"); + console.table(books); + console.warn("Your store is quite empty!"); + return books; + }; + } + }); +}; export const booksSchema = createGraphQLSchemaPlugin({ typeDefs: /* GraphQL */ ` diff --git a/packages/handler-graphql/__tests__/mocks/booksSchema.ts b/packages/handler-graphql/__tests__/mocks/booksSchema.ts index 27b8d5bd139..58e8eed7a7c 100644 --- a/packages/handler-graphql/__tests__/mocks/booksSchema.ts +++ b/packages/handler-graphql/__tests__/mocks/booksSchema.ts @@ -1,5 +1,6 @@ -import { createContextPlugin } from "@webiny/handler"; -import type { Book, Context } from "~tests/types"; +import type { Container } from "@webiny/di"; +import { RequestContextInitializer } from "@webiny/event-handler-core"; +import type { Book } from "~tests/types"; import { CoreGraphQLSchemaFactory } from "~/graphql/abstractions.js"; import type { GraphQLSchemaBuilder } from "~/features/GraphQLSchemaBuilder/abstractions.js"; @@ -90,15 +91,20 @@ export const BooksSchemaImpl = CoreGraphQLSchemaFactory.createImplementation({ dependencies: [] }); -export const booksCrudPlugin = createContextPlugin(async context => { - context.getBooks = async () => { - console.log("getBooks"); - console.table(books); - console.warn("Your store is quite empty!"); - return books; - }; -}); +// Augments the request context with `getBooks` post-auth (the Query.books resolver reads it). +export const booksCrudPlugin = (container: Container) => { + container.registerInstance(RequestContextInitializer, { + async init(context: Record) { + context.getBooks = async () => { + console.log("getBooks"); + console.table(books); + console.warn("Your store is quite empty!"); + return books; + }; + } + }); +}; -export const booksSchemaPlugin = createContextPlugin(context => { - context.container.register(BooksSchemaImpl); -}); +export const booksSchemaPlugin = (container: Container) => { + container.register(BooksSchemaImpl); +}; diff --git a/packages/handler-graphql/__tests__/useGqlHandler.ts b/packages/handler-graphql/__tests__/useGqlHandler.ts index 42c46363bc9..6a6810350c4 100644 --- a/packages/handler-graphql/__tests__/useGqlHandler.ts +++ b/packages/handler-graphql/__tests__/useGqlHandler.ts @@ -12,7 +12,17 @@ export default ({ plugins = [] }: Params = {}) => { root: () => {}, request: async container => { const flat = [plugins].flat(Infinity as 1).filter(Boolean); - registerLegacyPluginsViaGqlContextualSchema(container, flat); + // DI-native plugins are plain `container => {}` functions (they register features / + // request-context initializers directly). Call them here; everything else (legacy + // `graphql-schema` plugins) still goes through the bridge until #39 removes it. + const isFn = (p: any) => typeof p === "function" && !p.prototype; + for (const plugin of flat.filter(isFn)) { + (plugin as (container: any) => void)(container); + } + registerLegacyPluginsViaGqlContextualSchema( + container, + flat.filter(p => !isFn(p)) + ); GraphQLEngineFeature.register(container); }