Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/graphql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"!*/__tests__"
],
"dependencies": {
"@graphql-yoga/subscription": "^5.0.5",
"@loopback/boot": "^8.0.9",
"@loopback/core": "^7.0.8",
"@loopback/graphql": "^0.12.9",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createPubSub} from '@graphql-yoga/subscription';
import {createBindingFromClass} from '@loopback/core';
import {GraphQLBindings, GraphQLServer} from '@loopback/graphql';
import {expect, supertest} from '@loopback/testlab';
Expand Down Expand Up @@ -45,6 +46,7 @@ describe('GraphQL context', () => {
});

server.bind('recipes').to([...sampleRecipes]);
server.bind(GraphQLBindings.PUB_SUB).to(createPubSub());
const repoBinding = createBindingFromClass(RecipeRepository);
server.add(repoBinding);
server.add(createBindingFromClass(RecipesDataSource));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createPubSub} from '@graphql-yoga/subscription';
import {createBindingFromClass} from '@loopback/core';
import {GraphQLServer} from '@loopback/graphql';
import {GraphQLBindings, GraphQLServer} from '@loopback/graphql';
import {supertest} from '@loopback/testlab';
import {RecipesDataSource} from '../../datasources';
import {RecipeResolver} from '../../graphql-resolvers/recipe-resolver';
Expand Down Expand Up @@ -38,6 +39,7 @@ describe('GraphQL middleware', () => {
});
server.resolver(RecipeResolver);
server.bind('recipes').to([...sampleRecipes]);
server.bind(GraphQLBindings.PUB_SUB).to(createPubSub());
const repoBinding = createBindingFromClass(RecipeRepository);
server.add(repoBinding);
server.add(createBindingFromClass(RecipesDataSource));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createPubSub} from '@graphql-yoga/subscription';
import {createBindingFromClass} from '@loopback/core';
import {GraphQLBindings, GraphQLServer} from '@loopback/graphql';
import {expect, supertest} from '@loopback/testlab';
Expand Down Expand Up @@ -76,6 +77,7 @@ describe('GraphQL middleware', () => {
server = new GraphQLServer({host: '127.0.0.1', port: 0});
server.resolver(RecipeResolver);
server.bind('recipes').to([...sampleRecipes]);
server.bind(GraphQLBindings.PUB_SUB).to(createPubSub());
const repoBinding = createBindingFromClass(RecipeRepository);
server.add(repoBinding);
server.add(createBindingFromClass(RecipesDataSource));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createPubSub} from '@graphql-yoga/subscription';
import {Application, createBindingFromClass} from '@loopback/core';
import {GraphQLServer} from '@loopback/graphql';
import {GraphQLBindings, GraphQLServer} from '@loopback/graphql';
import {
createRestAppClient,
expect,
Expand Down Expand Up @@ -51,6 +52,7 @@ describe('GraphQL server subscription', () => {
server.bind('recipes').to([...sampleRecipes]);
const repoBinding = createBindingFromClass(RecipeRepository);
server.add(repoBinding);
server.bind(GraphQLBindings.PUB_SUB).to(createPubSub());
server.add(createBindingFromClass(RecipesDataSource));
server.add(createBindingFromClass(RecipeService));
await server.start();
Expand Down Expand Up @@ -99,6 +101,7 @@ describe('GraphQL application subscription', () => {
app.bind('recipes').to([...sampleRecipes]);
const repoBinding = createBindingFromClass(RecipeRepository);
app.add(repoBinding);
server.bind(GraphQLBindings.PUB_SUB).to(createPubSub());
app.add(createBindingFromClass(RecipesDataSource));
app.add(createBindingFromClass(RecipeService));
await app.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createPubSub} from '@graphql-yoga/subscription';
import {Application, createBindingFromClass} from '@loopback/core';
import {GraphQLServer} from '@loopback/graphql';
import {GraphQLBindings, GraphQLServer} from '@loopback/graphql';
import {
createRestAppClient,
givenHttpServerConfig,
Expand Down Expand Up @@ -35,6 +36,7 @@ describe('GraphQL server', () => {
server.resolver(RecipeResolver);

server.bind('recipes').to([...sampleRecipes]);
server.bind(GraphQLBindings.PUB_SUB).to(createPubSub());
const repoBinding = createBindingFromClass(RecipeRepository);
server.add(repoBinding);
server.add(createBindingFromClass(RecipesDataSource));
Expand Down Expand Up @@ -68,6 +70,7 @@ describe('GraphQL application', () => {
server.resolver(RecipeResolver);

app.bind('recipes').to([...sampleRecipes]);
server.bind(GraphQLBindings.PUB_SUB).to(createPubSub());
const repoBinding = createBindingFromClass(RecipeRepository);
app.add(repoBinding);
app.add(createBindingFromClass(RecipesDataSource));
Expand Down
3 changes: 3 additions & 0 deletions examples/graphql/src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createPubSub} from '@graphql-yoga/subscription';
import {BootMixin} from '@loopback/boot';
import {ApplicationConfig} from '@loopback/core';
import {GraphQLBindings, GraphQLComponent} from '@loopback/graphql';
Expand Down Expand Up @@ -30,6 +31,8 @@ export class GraphqlDemoApplication extends BootMixin(

this.expressMiddleware('middleware.express.GraphQL', server.expressApp);

this.bind(GraphQLBindings.PUB_SUB).to(createPubSub());

// It's possible to register a graphql context resolver
this.bind(GraphQLBindings.GRAPHQL_CONTEXT_RESOLVER).to(async context => {
return {...context};
Expand Down
12 changes: 5 additions & 7 deletions examples/graphql/src/graphql-resolvers/recipe-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import {
GraphQLBindings,
Int,
mutation,
Publisher,
pubSub,
PubSub,
query,
resolver,
ResolverData,
Expand All @@ -33,6 +32,8 @@ export class RecipeResolver implements ResolverInterface<Recipe> {
@repository('RecipeRepository')
private readonly recipeRepo: RecipeRepository,
@service(RecipeService) private readonly recipeService: RecipeService,
// inject pubSub for publishing a notification
@inject(GraphQLBindings.PUB_SUB) private pubSub: PubSub,
// It's possible to inject the resolver data
@inject(GraphQLBindings.RESOLVER_DATA) private resolverData: ResolverData,
) {}
Expand All @@ -49,12 +50,9 @@ export class RecipeResolver implements ResolverInterface<Recipe> {
}

@mutation(returns => Recipe)
async addRecipe(
@arg('recipe') recipe: RecipeInput,
@pubSub('recipeCreated') publish: Publisher<Recipe>,
): Promise<Recipe> {
async addRecipe(@arg('recipe') recipe: RecipeInput): Promise<Recipe> {
const result = await this.recipeRepo.add(recipe);
await publish(result);
this.pubSub.publish('recipeCreated', result);
return result;
}

Expand Down
4 changes: 2 additions & 2 deletions extensions/graphql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@
"debug": "^4.4.3",
"express": "^4.22.1",
"graphql": "^16.12.0",
"graphql-subscriptions": "^2.0.0",
"graphql-ws": "^5.16.2",
"type-graphql": "^2.0.0-beta.2",
"type-graphql": "^2.0.0-rc.2",
"ws": "^8.19.0"
},
"devDependencies": {
"@graphql-yoga/subscription": "^5.0.5",
"@loopback/boot": "^8.0.9",
"@loopback/build": "^12.0.8",
"@loopback/core": "^7.0.8",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {inject} from '@loopback/core';
import {
arg,
fieldResolver,
Publisher,
pubSub,
GraphQLBindings,
PubSub,
query,
resolver,
} from '../../../../';
Expand All @@ -17,7 +18,7 @@ import {Recipe} from './recipe.model';

@resolver(of => Recipe)
export class RecipeResolver {
constructor() {}
constructor(@inject(GraphQLBindings.PUB_SUB) private pubSub: PubSub) {}

@query(returns => [Recipe])
async recipes(): Promise<Recipe[]> {
Expand All @@ -30,13 +31,10 @@ export class RecipeResolver {
}

@mutation(returns => Recipe)
async addRecipe(
@arg('recipe') recipe: RecipeInput,
@pubSub('recipeCreated') publish: Publisher<Recipe>,
): Promise<Recipe> {
async addRecipe(@arg('recipe') recipe: RecipeInput): Promise<Recipe> {
const result = new Recipe();
result.title = recipe.title;
await publish(result);
this.pubSub.publish('recipeCreated', result);
return result;
}
}
5 changes: 1 addition & 4 deletions extensions/graphql/src/booters/resolver.booter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
} from '@loopback/core';
import debugFactory from 'debug';
import {getMetadataStorage} from 'type-graphql';
import {ResolverClassMetadata} from 'type-graphql/dist/metadata/definitions';
import {registerResolver} from '../graphql.server';

const debug = debugFactory('loopback:graphql:resolver-booter');
Expand Down Expand Up @@ -59,9 +58,7 @@ export class GraphQLResolverBooter extends BaseArtifactBooter {
async load() {
await super.load();

const resolverClasses: ResolverClassMetadata[] =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(getMetadataStorage() as any).resolverClasses;
const resolverClasses = getMetadataStorage().resolverClasses;
this.resolvers = this.classes.filter(cls => {
return resolverClasses.some(r => /*!r.isAbstract && */ r.target === cls);
});
Expand Down
2 changes: 0 additions & 2 deletions extensions/graphql/src/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
InputType,
Mutation,
ObjectType,
PubSub,
Query,
Resolver,
Root,
Expand All @@ -41,4 +40,3 @@ export const inputType = InputType;
export const objectType = ObjectType;
export const authorized = Authorized;
export const subscription = Subscription;
export const pubSub = PubSub;
16 changes: 5 additions & 11 deletions extensions/graphql/src/graphql.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import cors from 'cors';
import express from 'express';
import fs from 'fs';
import {GraphQLSchema, lexicographicSortSchema} from 'graphql';
import {PubSub, PubSubEngine} from 'graphql-subscriptions';
import {useServer} from 'graphql-ws/lib/use/ws';
import * as http from 'http';
import * as https from 'https';
Expand All @@ -44,7 +43,7 @@ import {
ResolverInterface,
BuildSchemaOptions as TypeGrahpQLBuildSchemaOptions,
} from 'type-graphql';
import {Middleware} from 'type-graphql/dist/interfaces/Middleware';
import {Middleware} from 'type-graphql/build/typings/typings/middleware';
import {WebSocketServer} from 'ws';
import {LoopBackContainer} from './graphql.container';
import {GraphQLBindings, GraphQLTags} from './keys';
Expand Down Expand Up @@ -99,8 +98,8 @@ export class GraphQLServer extends Context implements Server {
/**
* Get a list of middleware
*/
async getMiddlewareList(): Promise<Middleware<object>[]> {
const view = this.createView<Middleware<object>>(
async getMiddlewareList<T extends {}>(): Promise<Middleware<T>[]> {
const view = this.createView<Middleware<T>>(
filterByTag(GraphQLTags.MIDDLEWARE),
);
return view.values();
Expand All @@ -110,9 +109,7 @@ export class GraphQLServer extends Context implements Server {
* Register a GraphQL middleware
* @param middleware - GraphQL middleware
*/
middleware<T extends object = object>(
middleware: Middleware<T>,
): Binding<Middleware<T>> {
middleware<T extends {}>(middleware: Middleware<T>): Binding<Middleware<T>> {
return this.bind<Middleware<T>>(BindingKey.generate(`graphql.middleware`))
.to(middleware)
.tag(GraphQLTags.MIDDLEWARE);
Expand Down Expand Up @@ -140,10 +137,7 @@ export class GraphQLServer extends Context implements Server {
optional: true,
})) ?? ((resolverData, roles) => true);

const pubSub: PubSubEngine | undefined =
(await this.get(GraphQLBindings.PUB_SUB_ENGINE, {
optional: true,
})) ?? new PubSub();
const pubSub = await this.get(GraphQLBindings.PUB_SUB, {optional: true});

// build TypeGraphQL executable schema
const buildSchemaOptions: TypeGrahpQLBuildSchemaOptions = {
Expand Down
1 change: 0 additions & 1 deletion extensions/graphql/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
// License text available at https://opensource.org/licenses/MIT

export * from 'type-graphql';
export * from 'type-graphql/dist/interfaces';
export * from './decorators';
export * from './graphql.component';
export * from './graphql.container';
Expand Down
7 changes: 2 additions & 5 deletions extensions/graphql/src/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

import {ExpressMiddlewareOptions} from '@apollo/server/dist/esm/express4';
import {BindingKey, Constructor, CoreBindings} from '@loopback/core';
import {PubSubEngine} from 'graphql-subscriptions';
import {AuthChecker, ResolverData} from 'type-graphql';
import {AuthChecker, PubSub, ResolverData} from 'type-graphql';
import {GraphQLComponent} from './graphql.component';
import {GraphQLServer} from './graphql.server';
import {GraphQLServerOptions, GraphQLWsContextResolver} from './types';
Expand Down Expand Up @@ -55,9 +54,7 @@ export namespace GraphQLBindings {
/**
* Binding key for the GraphQL pub/sub engine
*/
export const PUB_SUB_ENGINE = BindingKey.create<PubSubEngine>(
'graphql.pubSubEngine',
);
export const PUB_SUB = BindingKey.create<PubSub>('graphql.pubSub');

/**
* Binding key for the GraphQL resolver data - which is bound per request
Expand Down
2 changes: 1 addition & 1 deletion extensions/graphql/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {ExecutionArgs} from 'graphql';
import {GraphQLExecutionContextValue, SubscribeMessage} from 'graphql-ws';

export {Float, ID, Int, ResolverInterface} from 'type-graphql';
export {Middleware as GraphQLMiddleware} from 'type-graphql/dist/interfaces/Middleware';
export {Middleware as GraphQLMiddleware} from 'type-graphql/build/typings/typings/middleware';

/**
* Options for GraphQL component
Expand Down
Loading