UNPKG

@graphile/pg-pubsub

Version:

Subscriptions plugin for PostGraphile using PostgreSQL's LISTEN/NOTIFY

135 lines 5.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const debug_1 = require("debug"); const graphql_subscriptions_1 = require("graphql-subscriptions"); const debug = (0, debug_1.default)("pg-pubsub"); function isPubSub(pubsub) { return !!pubsub; } function withInitialEvent(topic, initialEvent) { return function subscribeWithInitialEvent(asyncIterator, _parent, args, resolveContext, resolveInfo) { return (0, tslib_1.__asyncGenerator)(this, arguments, function* subscribeWithInitialEvent_1() { const event = yield (0, tslib_1.__await)(initialEvent(args, resolveContext, resolveInfo)); if (event != null) { if (typeof event !== "object") { throw new Error("event returned from pgSubscription initialEvent must be an object"); } yield yield (0, tslib_1.__await)(Object.assign(Object.assign({}, event), { topic })); } /* TODO: when we can upgrade to Node 10.3+ we can replace the below with simply: for await (const val of asyncIterator) { yield val; } */ try { while (true) { const next = yield (0, tslib_1.__await)(asyncIterator.next()); if (next.done) { return yield (0, tslib_1.__await)(next.value); } else { yield yield (0, tslib_1.__await)(next.value); } } } finally { // Terminate the previous iterator if (typeof asyncIterator.return === "function") { asyncIterator.return(); } } }); }; } /* * This plugin looks for the `@pgSubscription` directive, and adds the * `subscribe` method. */ const PgSubscriptionResolverPlugin = function (builder, { pubsub }) { if (!isPubSub(pubsub)) { debug("Subscriptions disabled - no pubsub provided"); return; } builder.hook("GraphQLObjectType:fields:field", (field, build, graphileContext) => { const { extend } = build; const { scope: { isRootSubscription, fieldDirectives }, } = graphileContext; if (!isRootSubscription) { return field; } if (!fieldDirectives) { return field; } const { pgSubscription } = fieldDirectives; if (!pgSubscription) { return field; } const { topic: topicGen, unsubscribeTopic: unsubscribeTopicGen, filter, initialEvent, } = pgSubscription; if (!topicGen) { return field; } const subscribe = async (parent, args, resolveContext, resolveInfo) => { const topic = typeof topicGen === "function" ? await topicGen(args, resolveContext, resolveInfo) : topicGen; if (!topic) { throw new Error("Cannot subscribe at this time"); } if (typeof topic !== "string") { throw new Error("Invalid topic provided to pgSubscription"); } const unsubscribeTopic = typeof unsubscribeTopicGen === "function" ? await unsubscribeTopicGen(args, resolveContext, resolveInfo) : unsubscribeTopicGen; let asyncIterator = pubsub.asyncIterator(topic); if (unsubscribeTopic) { // Subscribe to event revoking subscription const unsubscribeTopics = Array.isArray(unsubscribeTopic) ? unsubscribeTopic : [unsubscribeTopic]; const unsubscribeIterators = unsubscribeTopics.map(t => { const i = pubsub.asyncIterator(t); i["topic"] = t; return i; }); unsubscribeIterators.forEach(unsubscribeIterator => { unsubscribeIterator.next().then(() => { debug("Unsubscribe triggered on channel %s", unsubscribeIterator["topic"]); if (asyncIterator.return) { asyncIterator.return(); } unsubscribeIterators.forEach(i => { if (i.return) { i.return(); } }); }); }); } if (initialEvent) { if (typeof initialEvent !== "function") { throw new Error("initialEvent provided to pgSubscription must be a function"); } const oldAsyncIterator = asyncIterator; asyncIterator = withInitialEvent(topic, initialEvent)(oldAsyncIterator, parent, args, resolveContext, resolveInfo); } // filter should always be the last adjustment // since we want to feed all current and future // custom emissions to the filter if (filter) { if (typeof filter !== "function") { throw new Error("filter provided to pgSubscription must be a function"); } const oldAsyncIterator = asyncIterator; asyncIterator = (0, graphql_subscriptions_1.withFilter)(() => oldAsyncIterator, filter)(parent, args, resolveContext, resolveInfo); } return asyncIterator; }; const resolve = (event) => { return event; }; return extend(field, Object.assign({ subscribe }, (field.resolve ? null : { resolve }))); }, ["PgSubscriptionResolver"]); }; exports.default = PgSubscriptionResolverPlugin; //# sourceMappingURL=PgSubscriptionResolverPlugin.js.map