Skip to content

Commit

Permalink
feat(core): Enable GraphQL subscriptions for Apollo driver
Browse files Browse the repository at this point in the history
  • Loading branch information
dlhck committed Oct 20, 2024
1 parent d90a929 commit f7d2969
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 105 deletions.
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

212 changes: 107 additions & 105 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,107 +1,109 @@
{
"name": "@vendure/core",
"version": "3.1.0-next.3",
"description": "A modern, headless ecommerce framework",
"repository": {
"type": "git",
"url": "https://github.com/vendure-ecommerce/vendure/"
},
"keywords": [
"vendure",
"ecommerce",
"headless",
"graphql",
"typescript"
],
"homepage": "https://www.vendure.io/",
"funding": "https://github.com/sponsors/michaelbromley",
"private": false,
"license": "GPL-3.0-or-later",
"type": "commonjs",
"scripts": {
"tsc:watch": "tsc -p ./build/tsconfig.build.json --watch",
"copy:watch": "ts-node build/copy-static.ts watch",
"build": "rimraf dist && tsc -p ./build/tsconfig.build.json && tsc -p ./build/tsconfig.cli.json && ts-node build/copy-static.ts build",
"watch": "concurrently npm:tsc:watch npm:copy:watch",
"lint": "eslint --fix .",
"test": "vitest --config vitest.config.mts --run",
"e2e": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.mts --run",
"e2e:watch": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.mts",
"bench": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.bench.ts --run",
"ci": "npm run build"
},
"publishConfig": {
"access": "public"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist/**/*",
"cli/**/*"
],
"dependencies": {
"@apollo/server": "^4.10.4",
"@graphql-tools/stitch": "^9.2.10",
"@nestjs/apollo": "~12.2.0",
"@nestjs/common": "~10.3.10",
"@nestjs/core": "~10.3.10",
"@nestjs/graphql": "~12.2.0",
"@nestjs/platform-express": "~10.3.10",
"@nestjs/terminus": "~10.2.3",
"@nestjs/testing": "~10.3.10",
"@nestjs/typeorm": "~10.0.2",
"@types/fs-extra": "^9.0.1",
"@vendure/common": "3.1.0-next.3",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"cookie-session": "^2.1.0",
"csv-parse": "^5.5.5",
"express": "^4.18.3",
"fs-extra": "^11.2.0",
"graphql": "~16.9.0",
"graphql-fields": "^2.0.3",
"graphql-scalars": "^1.22.5",
"graphql-tag": "^2.12.6",
"graphql-upload": "^16.0.2",
"http-proxy-middleware": "^2.0.6",
"i18next": "^23.12.1",
"i18next-fs-backend": "^2.3.1",
"i18next-http-middleware": "^3.5.0",
"i18next-icu": "^2.3.0",
"image-size": "^1.1.1",
"intl-messageformat": "^10.5.11",
"mime-types": "^2.1.35",
"ms": "^2.1.3",
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"progress": "^2.0.3",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"semver": "^7.6.0",
"typeorm": "0.3.20"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/cookie-session": "^2.0.48",
"@types/csv-parse": "^1.2.2",
"@types/express": "^4.17.21",
"@types/graphql-upload": "^16.0.7",
"@types/gulp": "^4.0.17",
"@types/mime-types": "^2.1.4",
"@types/ms": "^0.7.34",
"@types/node": "^18.19.23",
"@types/progress": "^2.0.7",
"@types/prompts": "^2.4.9",
"@types/semver": "^7.5.8",
"better-sqlite3": "^11.4.0",
"chokidar": "^3.6.0",
"fs-extra": "^11.2.0",
"glob": "^10.3.10",
"mysql": "^2.18.1",
"pg": "^8.11.3",
"rimraf": "^5.0.5",
"sql.js": "1.10.2",
"sqlite3": "^5.1.7",
"typescript": "5.3.3"
}
"name": "@vendure/core",
"version": "3.1.0-next.3",
"description": "A modern, headless ecommerce framework",
"repository": {
"type": "git",
"url": "https://github.com/vendure-ecommerce/vendure/"
},
"keywords": [
"vendure",
"ecommerce",
"headless",
"graphql",
"typescript"
],
"homepage": "https://www.vendure.io/",
"funding": "https://github.com/sponsors/michaelbromley",
"private": false,
"license": "GPL-3.0-or-later",
"type": "commonjs",
"scripts": {
"tsc:watch": "tsc -p ./build/tsconfig.build.json --watch",
"copy:watch": "ts-node build/copy-static.ts watch",
"build": "rimraf dist && tsc -p ./build/tsconfig.build.json && tsc -p ./build/tsconfig.cli.json && ts-node build/copy-static.ts build",
"watch": "concurrently npm:tsc:watch npm:copy:watch",
"lint": "eslint --fix .",
"test": "vitest --config vitest.config.mts --run",
"e2e": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.mts --run",
"e2e:watch": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.mts",
"bench": "cross-env PACKAGE=core vitest --config ../../e2e-common/vitest.config.bench.ts --run",
"ci": "npm run build"
},
"publishConfig": {
"access": "public"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist/**/*",
"cli/**/*"
],
"dependencies": {
"@apollo/server": "^4.10.4",
"@graphql-tools/stitch": "^9.2.10",
"@nestjs/apollo": "~12.2.0",
"@nestjs/common": "~10.3.10",
"@nestjs/core": "~10.3.10",
"@nestjs/graphql": "~12.2.0",
"@nestjs/platform-express": "~10.3.10",
"@nestjs/terminus": "~10.2.3",
"@nestjs/testing": "~10.3.10",
"@nestjs/typeorm": "~10.0.2",
"@types/fs-extra": "^9.0.1",
"@vendure/common": "3.1.0-next.3",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"graphql-ws": "^5.16.0",
"graphql-subscriptions": "^2.0.0",
"cookie-session": "^2.1.0",
"csv-parse": "^5.5.5",
"express": "^4.18.3",
"fs-extra": "^11.2.0",
"graphql": "~16.9.0",
"graphql-fields": "^2.0.3",
"graphql-scalars": "^1.22.5",
"graphql-tag": "^2.12.6",
"graphql-upload": "^16.0.2",
"http-proxy-middleware": "^2.0.6",
"i18next": "^23.12.1",
"i18next-fs-backend": "^2.3.1",
"i18next-http-middleware": "^3.5.0",
"i18next-icu": "^2.3.0",
"image-size": "^1.1.1",
"intl-messageformat": "^10.5.11",
"mime-types": "^2.1.35",
"ms": "^2.1.3",
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"progress": "^2.0.3",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"semver": "^7.6.0",
"typeorm": "0.3.20"
},
"devDependencies": {
"@types/bcrypt": "^5.0.2",
"@types/cookie-session": "^2.0.48",
"@types/csv-parse": "^1.2.2",
"@types/express": "^4.17.21",
"@types/graphql-upload": "^16.0.7",
"@types/gulp": "^4.0.17",
"@types/mime-types": "^2.1.4",
"@types/ms": "^0.7.34",
"@types/node": "^18.19.23",
"@types/progress": "^2.0.7",
"@types/prompts": "^2.4.9",
"@types/semver": "^7.5.8",
"better-sqlite3": "^11.4.0",
"chokidar": "^3.6.0",
"fs-extra": "^11.2.0",
"glob": "^10.3.10",
"mysql": "^2.18.1",
"pg": "^8.11.3",
"rimraf": "^5.0.5",
"sql.js": "1.10.2",
"sqlite3": "^5.1.7",
"typescript": "5.3.3"
}
}
8 changes: 8 additions & 0 deletions packages/core/src/api/config/configure-graphql-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ async function createGraphQLOptions(
plugins: apolloServerPlugins,
validationRules: options.validationRules,
introspection: configService.apiOptions.introspection ?? true,
subscriptions: {
'graphql-ws': {
path: '/' + options.apiPath,
},
'subscriptions-transport-ws': {
path: '/' + options.apiPath,
},
},
} as ApolloDriverConfig;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ type Mutation {
assignRoleToAdministrator(administratorId: ID!, roleId: ID!): Administrator!
}

type Subscription {
ping: String!
}

# generated by generateListOptions function
input AdministratorListOptions

Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/api/schema/shop-api/shop.api.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ type Mutation {
resetPassword(token: String!, password: String!): ResetPasswordResult!
}

type Subscription {
ping: String!
}

# Populated at run-time
input AuthenticationInput

Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/plugin/plugin-common.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Module } from '@nestjs/common';
import { PubSub } from 'graphql-subscriptions';

import { CacheModule } from '../cache/cache.module';
import { ConfigModule } from '../config/config.module';
Expand Down Expand Up @@ -51,6 +52,13 @@ import { ServiceModule } from '../service/service.module';
I18nModule,
ProcessContextModule,
DataImportModule,
'PUB_SUB',
],
providers: [
{
provide: 'PUB_SUB',
useValue: new PubSub(),
},
],
})
export class PluginCommonModule {}
2 changes: 2 additions & 0 deletions packages/dev-server/dev-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import path from 'path';
import { DataSourceOptions } from 'typeorm';

import { MultivendorPlugin } from './example-plugins/multivendor-plugin/multivendor.plugin';
import { GraphqlSubscriptionsPlugin } from './test-plugins/graphql-subscriptions/graphql-subscriptions-plugin';

/**
* Config settings used during development
Expand Down Expand Up @@ -137,6 +138,7 @@ export const devConfig: VendureConfig = {
// devMode: true,
// }),
}),
GraphqlSubscriptionsPlugin,
],
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Args, Mutation, Resolver, Subscription } from '@nestjs/graphql';
import {
Ctx,
ID,
Order,
OrderService,
PluginCommonModule,
RequestContext,
TransactionalConnection,
VendurePlugin,
} from '@vendure/core';
import { PubSub } from 'graphql-subscriptions';
import { gql } from 'graphql-tag';
import { Inject } from '@nestjs/common';

@Resolver()
class OrderStateResolver {
constructor(
@Inject('PUB_SUB') private readonly pubSub: PubSub,
private readonly db: TransactionalConnection,
) {}

@Subscription(() => Order, {
filter: (payload, variables) => {
return payload.orderStateUpdated.id === variables.orderId;
},
})
async orderStateUpdated(@Args('orderId') orderId: ID) {
return this.pubSub.asyncIterator('orderStateUpdated');
}

@Mutation(() => Order)
async triggerOrderStateUpdated(@Ctx() ctx: RequestContext, @Args('orderId') orderId: ID) {
const order = await this.db.getEntityOrThrow(ctx, Order, orderId);

return this.pubSub.publish('orderStateUpdated', { orderStateUpdated: order });
}
}

@VendurePlugin({
imports: [PluginCommonModule],
shopApiExtensions: {
schema: gql`
extend type Subscription {
orderStateUpdated(orderId: ID!): Order!
}
extend type Mutation {
triggerOrderStateUpdated(orderId: ID!): Order
}
`,
resolvers: [OrderStateResolver],
},
configuration: config => {
return config;
},
})
export class GraphqlSubscriptionsPlugin {}

0 comments on commit f7d2969

Please sign in to comment.