nestjs-google-pubsub-microservice
Version:
NestJS Google Cloud Pub/Sub Microservice Transport
216 lines • 8.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GCPubSubClient = void 0;
const pubsub_1 = require("@google-cloud/pubsub");
const common_1 = require("@nestjs/common");
const microservices_1 = require("@nestjs/microservices");
const gc_pubsub_constants_1 = require("./gc-pubsub.constants");
const gc_pubsub_utils_1 = require("./gc-pubsub.utils");
class GCPubSubClient extends microservices_1.ClientProxy {
constructor(options) {
super();
this.options = options;
this.logger = new common_1.Logger(GCPubSubClient.name);
this.client = null;
this.replySubscription = null;
this.topic = null;
this.clientConfig = this.options.client || gc_pubsub_constants_1.GC_PUBSUB_DEFAULT_CLIENT_CONFIG;
this.scopedEnvKey = this.options.scopedEnvKey ?? '';
this.topicName = this.options.topic || gc_pubsub_constants_1.GC_PUBSUB_DEFAULT_TOPIC;
this.topicName = `${this.scopedEnvKey}${this.topicName}`;
this.subscriberConfig =
this.options.subscriber || gc_pubsub_constants_1.GC_PUBSUB_DEFAULT_SUBSCRIBER_CONFIG;
this.publisherConfig =
this.options.publisher || gc_pubsub_constants_1.GC_PUBSUB_DEFAULT_PUBLISHER_CONFIG;
this.replyTopicName = this.options.replyTopic
? `${this.scopedEnvKey}${this.options.replyTopic}`
: undefined;
this.replySubscriptionName = this.options.replySubscription
? `${this.scopedEnvKey}${this.options.replySubscription}`
: undefined;
this.noAck = this.options.noAck ?? gc_pubsub_constants_1.GC_PUBSUB_DEFAULT_NO_ACK;
this.init = this.options.init ?? gc_pubsub_constants_1.GC_PUBSUB_DEFAULT_INIT;
this.useAttributes =
this.options.useAttributes ?? gc_pubsub_constants_1.GC_PUBSUB_DEFAULT_USE_ATTRIBUTES;
this.checkExistence =
this.options.checkExistence ?? gc_pubsub_constants_1.GC_PUBSUB_DEFAULT_CHECK_EXISTENCE;
this.replySubscriptionFilter = this.options.replySubscriptionFilter;
this.initializeSerializer(options);
this.initializeDeserializer(options);
}
async close() {
await (0, gc_pubsub_utils_1.flushTopic)(this.topic);
await (0, gc_pubsub_utils_1.closeSubscription)(this.replySubscription);
await (0, gc_pubsub_utils_1.closePubSub)(this.client);
this.client = null;
this.topic = null;
this.replySubscription = null;
}
async connect() {
if (this.client) {
return this.client;
}
this.client = this.createClient();
this.topic = this.client.topic(this.topicName, this.publisherConfig);
if (this.init) {
await this.createIfNotExists(this.topic.create.bind(this.topic));
}
else if (this.checkExistence) {
const [topicExists] = await this.topic.exists();
if (!topicExists) {
const message = `PubSub client is not connected: topic ${this.topicName} does not exist`;
this.logger.error(message);
throw new Error(message);
}
}
if (this.replyTopicName && this.replySubscriptionName) {
const replyTopic = this.client.topic(this.replyTopicName);
if (this.init) {
await this.createIfNotExists(replyTopic.create.bind(replyTopic));
}
else if (this.checkExistence) {
const [exists] = await replyTopic.exists();
if (!exists) {
const message = `PubSub client is not connected: topic ${this.replyTopicName} does not exist`;
this.logger.error(message);
throw new Error(message);
}
}
this.replySubscription = replyTopic.subscription(this.replySubscriptionName, this.subscriberConfig);
if (this.init) {
await this.createIfNotExists(this.replySubscriptionFilter
? () => this.client.createSubscription(this.replyTopicName, this.replySubscriptionName, { filter: this.replySubscriptionFilter })
: this.replySubscription.create.bind(this.replySubscription));
}
else if (this.checkExistence) {
const [exists] = await this.replySubscription.exists();
if (!exists) {
const message = `PubSub client is not connected: subscription ${this.replySubscription} does not exist`;
this.logger.error(message);
throw new Error(message);
}
}
this.replySubscription
.on('message', async (message) => {
try {
const isHandled = await this.handleResponse(message);
if (!isHandled) {
message.nack();
}
else if (this.noAck) {
message.ack();
}
}
catch (error) {
this.logger.error(error);
if (this.noAck) {
message.nack();
}
}
})
.on('error', (err) => this.logger.error(err));
}
return this.client;
}
createClient() {
return new pubsub_1.PubSub(this.clientConfig);
}
async dispatchEvent(packet) {
const pattern = this.normalizePattern(packet.pattern);
if (!this.topic) {
return;
}
const serializedPacket = this.serializer.serialize({
...packet,
pattern,
});
if (this.useAttributes) {
await this.topic.publishMessage({
json: serializedPacket.data,
attributes: {
pattern: serializedPacket.pattern,
},
});
}
else {
await this.topic.publishMessage({ json: serializedPacket });
}
}
publish(partialPacket, callback) {
try {
const packet = this.assignPacketId(partialPacket);
const serializedPacket = this.serializer.serialize(packet);
this.routingMap.set(packet.id, callback);
if (this.topic) {
if (this.useAttributes) {
this.topic
.publishMessage({
json: serializedPacket.data,
attributes: {
replyTo: this.replyTopicName,
pattern: serializedPacket.pattern,
id: serializedPacket.id,
},
})
.catch((err) => callback({ err }));
}
else {
this.topic
.publishMessage({
json: serializedPacket,
attributes: { replyTo: this.replyTopicName },
})
.catch((err) => callback({ err }));
}
}
else {
callback({ err: new Error('Topic is not created') });
}
return () => this.routingMap.delete(packet.id);
}
catch (err) {
callback({ err });
}
}
async handleResponse(message) {
const rawMessage = JSON.parse(message.data.toString());
const { err, response, isDisposed, id } = this.deserializer.deserialize(rawMessage);
const correlationId = message.attributes.id || id;
const callback = this.routingMap.get(correlationId);
if (!callback) {
return false;
}
if (err || isDisposed) {
callback({
err,
response,
isDisposed,
});
}
else {
callback({
err,
response,
});
}
return true;
}
async createIfNotExists(create) {
try {
await create();
}
catch (error) {
if (error.code !== gc_pubsub_constants_1.ALREADY_EXISTS) {
throw error;
}
}
}
unwrap() {
if (!this.client) {
throw new Error('Client is not initialized.');
}
return this.client;
}
}
exports.GCPubSubClient = GCPubSubClient;
//# sourceMappingURL=gc-pubsub.client.js.map