diff --git a/package-lock.json b/package-lock.json index 8584eaa..f98e993 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "polyapi", - "version": "0.24.20", + "version": "0.24.21", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "polyapi", - "version": "0.24.20", + "version": "0.24.21", "license": "MIT", "dependencies": { "@guanghechen/helper-string": "4.7.1", diff --git a/package.json b/package.json index 5c17d92..82b0a1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "polyapi", - "version": "0.24.20", + "version": "0.24.21", "description": "Poly is a CLI tool to help create and manage your Poly definitions.", "license": "MIT", "repository": { diff --git a/src/commands/generate/index.ts b/src/commands/generate/index.ts index 5c9fca8..27a7a36 100644 --- a/src/commands/generate/index.ts +++ b/src/commands/generate/index.ts @@ -8,6 +8,7 @@ import { ApiFunctionSpecification, AuthFunctionSpecification, CustomFunctionSpecification, + GraphQLSubscriptionSpecification, ServerFunctionSpecification, ServerVariableSpecification, Specification, @@ -62,6 +63,7 @@ const prepareDir = async (polyPath: string) => { fs.mkdirSync(`${libPath}/client`); fs.mkdirSync(`${libPath}/auth`); fs.mkdirSync(`${libPath}/webhooks`); + fs.mkdirSync(`${libPath}/subscriptions`); fs.mkdirSync(`${libPath}/server`); fs.mkdirSync(`${libPath}/vari`); fs.mkdirSync(`${libPath}/tabi`); @@ -144,6 +146,9 @@ const generateJSFiles = async ( const tables = specs.filter( (spec) => spec.type === 'table', ) as TableSpecification[]; + const gqlSubscriptions = specs.filter( + (spec) => spec.type === 'graphqlSubscription', + ) as GraphQLSubscriptionSpecification[]; await generateIndexJSFile(libPath); await generatePolyCustomJSFile(libPath); @@ -158,6 +163,7 @@ const generateJSFiles = async ( 'custom functions', ); await tryAsync(generateWebhooksJSFiles(libPath, webhookHandles), 'webhooks'); + await tryAsync(generateGraphQLSubscriptionJSFiles(libPath, gqlSubscriptions), 'GraphQL subscriptions'); await tryAsync( generateAuthFunctionJSFiles(libPath, authFunctions), 'auth functions', @@ -284,6 +290,21 @@ const generateWebhooksJSFiles = async ( fs.copyFileSync(templateUrl('webhooks-index.js'), `${libPath}/webhooks/index.js`); }; +const generateGraphQLSubscriptionJSFiles = async ( + libPath: string, + specifications: GraphQLSubscriptionSpecification[], +) => { + const template = handlebars.compile(loadTemplate('graphql-subscriptions.js.hbs')); + fs.writeFileSync( + `${libPath}/subscriptions/subscriptions.js`, + template({ + specifications, + apiKey: getApiKey(), + }), + ); + fs.copyFileSync(templateUrl('graphql-subscriptions-index.js'), `${libPath}/subscriptions/index.js`); +} + const generateServerFunctionJSFiles = async ( libPath: string, specifications: ServerFunctionSpecification[], diff --git a/src/commands/generate/types.ts b/src/commands/generate/types.ts index 42f81a6..f207866 100644 --- a/src/commands/generate/types.ts +++ b/src/commands/generate/types.ts @@ -420,6 +420,8 @@ const getIDComment = (specification: Specification) => { return `* Auth provider ID: ${specification.id}`; case 'webhookHandle': return `* Webhook ID: ${specification.id}`; + case 'graphqlSubscription': + return `* GraphQL Subscription ID: ${specification.id}`; default: return null; } diff --git a/src/types/specifications.ts b/src/types/specifications.ts index 5b3acff..a903717 100644 --- a/src/types/specifications.ts +++ b/src/types/specifications.ts @@ -14,6 +14,7 @@ export type SpecificationType = | 'serverVariable' | 'snippet' | 'table' + | 'graphqlSubscription' | 'schema'; export interface ISpecification { @@ -126,6 +127,12 @@ export interface WebhookHandleSpecification extends ISpecification { function: FunctionSpecification; } +export interface GraphQLSubscriptionSpecification extends ISpecification { + type: 'graphqlSubscription'; + description: string; + function: FunctionSpecification; +} + export interface ServerVariableSpecification extends ISpecification { type: 'serverVariable'; variable: VariableSpecification; @@ -170,6 +177,7 @@ export type Specification = | ServerVariableSpecification | SnippetSpecification | SchemaSpecification + | GraphQLSubscriptionSpecification | TableSpecification; interface CreateWebhookHandleDtoForSpecificationInput diff --git a/src/utils.ts b/src/utils.ts index fb85dba..27368ce 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -54,15 +54,17 @@ export type GenerationError = { }; export const echoGenerationError = (specification: Specification) => { - const typeMap = { + const typeMap: Record = { apiFunction: 'API Function', customFunction: 'Custom Function', authFunction: 'Auth Function', webhookHandle: 'Webhook Handle', + graphqlSubscription: 'Webhook Handle', serverFunction: 'Server Function', serverVariable: 'Variable', schema: 'Schema', snippet: 'Snippet', + table: 'Table' }; const type = typeMap[specification.type]; diff --git a/templates/graphql-subscriptions-index.js b/templates/graphql-subscriptions-index.js new file mode 100644 index 0000000..deb3a63 --- /dev/null +++ b/templates/graphql-subscriptions-index.js @@ -0,0 +1,35 @@ +const set = require('lodash/set'); +const { subscriptions } = require('./subscriptions'); + +const registerGraphQLSubscriptionEventListener = (clientID, getSocket, getApiKey, subscriptionId, callback) => { + const socket = getSocket(); + socket.emit('registerSubscriptionHandler', { + clientID, + subscriptionId, + apiKey: getApiKey(), + }, registered => { + if (registered) { + socket.on( + `handleSubscriptionEvent:${subscriptionId}`, + async ({ event, params, executionId }) => { + await callback(event, params, { executionId }); + } + ); + } else { + console.log(`Could not register GraphQL subscription event handler for ${subscriptionId}`); + } + }); + + return () => { + socket.emit('unregisterSubscriptionHandler', { + clientID, + subscriptionId, + apiKey: getApiKey(), + }); + } +}; + +module.exports = (clientID, getSocket, getApiKey) => subscriptions.reduce( + (acc, [path, id]) => set(acc, path, (callback) => registerGraphQLSubscriptionEventListener(clientID, getSocket, getApiKey, id, callback)), + {} +); diff --git a/templates/graphql-subscriptions.js.hbs b/templates/graphql-subscriptions.js.hbs new file mode 100644 index 0000000..7353133 --- /dev/null +++ b/templates/graphql-subscriptions.js.hbs @@ -0,0 +1,11 @@ +module.exports = { + subscriptions: [ + {{#each specifications}} + {{#if context}} + ['{{context}}.{{name}}', '{{id}}'], + {{else}} + ['{{name}}', '{{id}}'], + {{/if}} + {{/each}} + ] +} diff --git a/templates/index.d.ts.hbs b/templates/index.d.ts.hbs index 4cd088b..9581d00 100644 --- a/templates/index.d.ts.hbs +++ b/templates/index.d.ts.hbs @@ -15,6 +15,7 @@ declare const poly: Poly; export default poly; export type UnregisterWebhookEventListener = () => void; +export type UnregisterGraphQLSubscriptionEventListener = () => void; export type AuthFunctionCallback = (token?: string, url?: string, error?: any) => any; export interface AuthFunctionResponse { diff --git a/templates/index.js b/templates/index.js index 2830db8..40def6b 100644 --- a/templates/index.js +++ b/templates/index.js @@ -3,6 +3,7 @@ const { io } = require('socket.io-client'); const apiFunctions = require('./api'); const clientFunctions = require('./client'); const webhooks = require('./webhooks'); +const subscriptions = require('./subscriptions'); const authFunctions = require('./auth'); const serverFunctions = require('./server'); const vari = require('./vari'); @@ -68,6 +69,7 @@ merge( serverFunctions(CLIENT_ID, polyCustom), authFunctions(CLIENT_ID, getSocket, getApiKey), webhooks(CLIENT_ID, getSocket, getApiKey), + subscriptions(CLIENT_ID, getSocket, getApiKey), ), module.exports = { ...poly,