@graphile/pg-pubsub
Version:
Subscriptions plugin for PostGraphile using PostgreSQL's LISTEN/NOTIFY
135 lines • 5.93 kB
JavaScript
;
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