diff --git a/examples/graphql/src/__tests__/acceptance/graphql-context.acceptance.ts b/examples/graphql/src/__tests__/acceptance/graphql-context.acceptance.ts new file mode 100644 index 000000000000..f58e551740db --- /dev/null +++ b/examples/graphql/src/__tests__/acceptance/graphql-context.acceptance.ts @@ -0,0 +1,62 @@ +// Copyright IBM Corp. 2020. All Rights Reserved. +// Node module: @loopback/example-graphql +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +import {createBindingFromClass} from '@loopback/core'; +import {GraphQLBindings, GraphQLServer} from '@loopback/graphql'; +import {expect, supertest} from '@loopback/testlab'; +import {RecipesDataSource} from '../../datasources'; +import {RecipeResolver} from '../../graphql-resolvers/recipe-resolver'; +import {RecipeRepository} from '../../repositories'; +import {sampleRecipes} from '../../sample-recipes'; +import {RecipeService} from '../../services/recipe.service'; +import {exampleQuery} from './graphql-tests'; + +describe('GraphQL context', () => { + let server: GraphQLServer; + let repo: RecipeRepository; + + before(givenServer); + after(stopServer); + + it('invokes middleware', async () => { + await supertest(server.httpServer?.url) + .post('/graphql') + .set('content-type', 'application/json') + .accept('application/json') + .send({operationName: 'GetRecipe1', variables: {}, query: exampleQuery}) + .expect(200); + }); + + async function givenServer() { + server = new GraphQLServer({host: '127.0.0.1', port: 0}); + server.resolver(RecipeResolver); + + // Customize the GraphQL context with additional information for test verification + server.bind(GraphQLBindings.GRAPHQL_CONTEXT_RESOLVER).to(ctx => { + return {...ctx, meta: 'loopback'}; + }); + + // Register a GraphQL middleware to verify context resolution + server.middleware((resolverData, next) => { + expect(resolverData.context).to.containEql({meta: 'loopback'}); + return next(); + }); + + server.bind('recipes').to([...sampleRecipes]); + const repoBinding = createBindingFromClass(RecipeRepository); + server.add(repoBinding); + server.add(createBindingFromClass(RecipesDataSource)); + server.add(createBindingFromClass(RecipeService)); + await server.start(); + repo = await server.get(repoBinding.key); + await repo.start(); + } + + async function stopServer() { + if (!server) return; + await server.stop(); + repo.stop(); + } +}); diff --git a/examples/graphql/src/__tests__/acceptance/graphql-middleware.acceptance.ts b/examples/graphql/src/__tests__/acceptance/graphql-middleware.acceptance.ts index 1a0e21e80680..a460f2482510 100644 --- a/examples/graphql/src/__tests__/acceptance/graphql-middleware.acceptance.ts +++ b/examples/graphql/src/__tests__/acceptance/graphql-middleware.acceptance.ts @@ -3,26 +3,71 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {createRestAppClient, givenHttpServerConfig} from '@loopback/testlab'; -import {GraphqlDemoApplication} from '../../'; -import {runTests} from './graphql-tests'; +import {createBindingFromClass} from '@loopback/core'; +import {GraphQLServer} from '@loopback/graphql'; +import {expect, supertest} from '@loopback/testlab'; +import {RecipesDataSource} from '../../datasources'; +import {RecipeResolver} from '../../graphql-resolvers/recipe-resolver'; +import {RecipeRepository} from '../../repositories'; +import {sampleRecipes} from '../../sample-recipes'; +import {RecipeService} from '../../services/recipe.service'; +import {exampleQuery} from './graphql-tests'; describe('GraphQL middleware', () => { - let app: GraphqlDemoApplication; + let server: GraphQLServer; + let repo: RecipeRepository; - before(giveAppWithGraphQLMiddleware); - after(stopApp); + const fieldNamesCapturedByMiddleware: string[] = []; - runTests(() => createRestAppClient(app)); + before(givenServer); + after(stopServer); - async function giveAppWithGraphQLMiddleware() { - app = new GraphqlDemoApplication({rest: givenHttpServerConfig()}); - await app.boot(); - await app.start(); - return app; + it('invokes middleware', async () => { + await supertest(server.httpServer?.url) + .post('/graphql') + .set('content-type', 'application/json') + .accept('application/json') + .send({operationName: 'GetRecipe1', variables: {}, query: exampleQuery}) + .expect(200); + expect(fieldNamesCapturedByMiddleware).to.eql([ + // the query + 'recipe', + // field resolvers + 'title', + 'description', + 'ratings', + 'creationDate', + 'ratingsCount', + 'averageRating', + 'ingredients', + 'numberInCollection', + ]); + }); + + async function givenServer() { + server = new GraphQLServer({host: '127.0.0.1', port: 0}); + server.resolver(RecipeResolver); + + // Register a GraphQL middleware + server.middleware((resolverData, next) => { + // It's invoked for each field resolver + fieldNamesCapturedByMiddleware.push(resolverData.info.fieldName); + return next(); + }); + + server.bind('recipes').to([...sampleRecipes]); + const repoBinding = createBindingFromClass(RecipeRepository); + server.add(repoBinding); + server.add(createBindingFromClass(RecipesDataSource)); + server.add(createBindingFromClass(RecipeService)); + await server.start(); + repo = await server.get(repoBinding.key); + await repo.start(); } - async function stopApp() { - await app?.stop(); + async function stopServer() { + if (!server) return; + await server.stop(); + repo.stop(); } }); diff --git a/examples/graphql/src/__tests__/acceptance/graphql-tests.ts b/examples/graphql/src/__tests__/acceptance/graphql-tests.ts index b76446ff4daa..e82b3919a9e9 100644 --- a/examples/graphql/src/__tests__/acceptance/graphql-tests.ts +++ b/examples/graphql/src/__tests__/acceptance/graphql-tests.ts @@ -25,7 +25,7 @@ export function runTests(getClient: () => supertest.SuperTest) { .post('/graphql') .set('content-type', 'application/json') .accept('application/json') - .send({operationName: 'GetRecipe1', variables: {}, query: example}) + .send({operationName: 'GetRecipe1', variables: {}, query: exampleQuery}) .expect(200, expectedData); }); @@ -34,7 +34,7 @@ export function runTests(getClient: () => supertest.SuperTest) { .post('/graphql') .set('content-type', 'application/json') .accept('application/json') - .send({operationName: 'AddRecipe', variables: {}, query: example}) + .send({operationName: 'AddRecipe', variables: {}, query: exampleQuery}) .expect(200); expect(res.body.data.addRecipe).to.containEql({ id: '4', @@ -81,13 +81,13 @@ export function runTests(getClient: () => supertest.SuperTest) { .post('/graphql') .set('content-type', 'application/json') .accept('application/json') - .send({operationName: 'GetRecipes', variables: {}, query: example}) + .send({operationName: 'GetRecipes', variables: {}, query: exampleQuery}) .expect(200); expect(res.body.data.recipes).to.eql(expectedRecipes); }); } -const example = `query GetRecipe1 { +export const exampleQuery = `query GetRecipe1 { recipe(recipeId: "1") { title description diff --git a/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts b/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts index d7c064da3ce4..b48247e63f63 100644 --- a/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts +++ b/examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts @@ -5,7 +5,12 @@ import {Application, createBindingFromClass} from '@loopback/core'; import {GraphQLServer} from '@loopback/graphql'; -import {supertest} from '@loopback/testlab'; +import { + createRestAppClient, + givenHttpServerConfig, + supertest, +} from '@loopback/testlab'; +import {GraphqlDemoApplication} from '../../'; import {RecipesDataSource} from '../../datasources'; import {RecipeResolver} from '../../graphql-resolvers/recipe-resolver'; import {RecipeRepository} from '../../repositories'; @@ -72,3 +77,23 @@ describe('GraphQL application', () => { await app.stop(); } }); + +describe('GraphQL as middleware', () => { + let app: GraphqlDemoApplication; + + before(giveAppWithGraphQLMiddleware); + after(stopApp); + + runTests(() => createRestAppClient(app)); + + async function giveAppWithGraphQLMiddleware() { + app = new GraphqlDemoApplication({rest: givenHttpServerConfig()}); + await app.boot(); + await app.start(); + return app; + } + + async function stopApp() { + await app?.stop(); + } +});