graphql-amqp-subscriptions
Version:
GraphQL AMQP Subscriptions
119 lines • 4.73 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AMQPPubSub = void 0;
const debug_1 = __importDefault(require("debug"));
const pubsub_async_iterable_iterator_1 = require("graphql-subscriptions/dist/pubsub-async-iterable-iterator");
const uuid_1 = require("uuid");
const publisher_1 = require("./amqp/publisher");
const subscriber_1 = require("./amqp/subscriber");
const logger = (0, debug_1.default)('AMQPPubSub');
class AMQPPubSub {
publisher;
subscriber;
exchange;
subscriptionMap;
subsRefsMap;
unsubscribeMap;
currentSubscriptionId;
constructor(config) {
this.subscriptionMap = {};
this.subsRefsMap = {};
this.unsubscribeMap = {};
this.currentSubscriptionId = 0;
// Initialize AMQP Helper
this.publisher = new publisher_1.AMQPPublisher(config, logger);
this.subscriber = new subscriber_1.AMQPSubscriber(config, logger);
this.exchange = {
name: 'graphql_subscriptions',
type: 'topic',
options: {
durable: false,
autoDelete: false
},
...config.exchange
};
logger('Finished initializing');
}
async publish(routingKey, payload, options) {
logger('Publishing message to exchange "%s" for key "%s" (%j)', this.exchange.name, routingKey, payload);
return this.publisher.publish(routingKey, payload, options);
}
async subscribe(routingKey, onMessage, arguments_, options) {
const id = this.currentSubscriptionId++;
if (routingKey === 'fanout') {
routingKey = (0, uuid_1.v4)();
}
logger('Subscribing to "%s" with id: "%s"', routingKey, id);
this.subscriptionMap[id] = {
routingKey,
listener: onMessage
};
const references = this.subsRefsMap[routingKey];
if (references && references.length > 0) {
const newReferences = [...references, id];
this.subsRefsMap[routingKey] = newReferences;
return id;
}
this.subsRefsMap[routingKey] = [
...(this.subsRefsMap[routingKey] || []),
id
];
const existingDispose = this.unsubscribeMap[routingKey];
// Get rid of exisiting subscription while we get a new one.
const [newDispose] = await Promise.all([
this.subscriber.subscribe(routingKey, this.onMessage, arguments_, options),
existingDispose ? existingDispose() : Promise.resolve()
]);
this.unsubscribeMap[routingKey] = newDispose;
return id;
}
async unsubscribe(subId) {
const sub = this.subscriptionMap[subId];
if (!sub) {
throw new Error(`There is no subscription for id "${subId}"`);
}
const { routingKey } = sub;
const references = this.subsRefsMap[routingKey];
if (!references) {
throw new Error(`There is no subscription ref for routing key "${routingKey}", id "${subId}"`);
}
logger('Unsubscribing from "%s" with id: "%s"', routingKey, subId);
if (references.length === 1) {
delete this.subscriptionMap[subId];
return this.unsubscribeForKey(routingKey);
}
const index = references.indexOf(subId);
const newReferences = index === -1
? references
: [...references.slice(0, index), ...references.slice(index + 1)];
this.subsRefsMap[routingKey] = newReferences;
delete this.subscriptionMap[subId];
}
asyncIterableIterator(triggers) {
return new pubsub_async_iterable_iterator_1.PubSubAsyncIterableIterator(this, triggers);
}
onMessage = (routingKey, content, message) => {
const subscribers = this.subsRefsMap[routingKey];
// Don't work for nothing...
if (!subscribers || subscribers.length === 0) {
this.unsubscribeForKey(routingKey).catch((error) => {
logger('onMessage unsubscribeForKey error "%j", Routing Key "%s"', error, routingKey);
});
return;
}
for (const subId of subscribers) {
this.subscriptionMap[subId].listener(content, message);
}
};
async unsubscribeForKey(routingKey) {
const dispose = this.unsubscribeMap[routingKey];
delete this.unsubscribeMap[routingKey];
delete this.subsRefsMap[routingKey];
await dispose();
}
}
exports.AMQPPubSub = AMQPPubSub;
//# sourceMappingURL=pubsub.js.map