UNPKG

@sebspark/pubsub

Version:

A wrapper around [@google-cloud/pubsub](https://www.npmjs.com/package/@google-cloud/pubsub) adding simple methods for publishing and subscribing with typed messages.

171 lines (169 loc) 5.05 kB
// src/lib/publisher.ts import { Encodings, PubSub, SchemaTypes } from "@google-cloud/pubsub"; import { Type } from "avsc"; var schemaIdPattern = /^(?!goog)[a-zA-Z][a-zA-Z0-9-._~%+]{2,254}$/; var syncTopicSchema = async (client, cloudSchema) => { if (!schemaIdPattern.test(cloudSchema.schemaId)) { throw Error( "schemaId is no in a valid format. Check google cloud platform for more information" ); } const schema = client.schema(cloudSchema.schemaId); const exits = await schemaExists(client, cloudSchema.schemaId); if (exits) { const data2 = await schema.get(); return data2; } await client.createSchema( cloudSchema.schemaId, SchemaTypes.Avro, cloudSchema.avroDefinition ); const data = await schema.get(); return data; }; var createOrGetTopic = async (client, name, schemaData) => { const [exists] = await client.topic(name).exists(); if (exists) { const [topic2] = await client.topic(name).get(); return topic2; } if (schemaData) { const [topic2] = await client.createTopic({ name, schemaSettings: { schema: schemaData?.name, encoding: Encodings.Binary } }); return topic2; } const [topic] = await client.createTopic({ name }); return topic; }; var createPublisher = (clientOptions) => { const client = clientOptions ? new PubSub(clientOptions) : new PubSub(); let _topic; let _type; const ensureInitiated = async (name, schema) => { if (!_topic) { if (schema) { const schemaData = await syncTopicSchema(client, schema); _topic = await createOrGetTopic(client, name, schemaData); } _topic = await createOrGetTopic(client, name); } if (schema && !_type) { const schemaType = Type.forSchema(JSON.parse(schema.avroDefinition)); _type = schemaType; } }; const typedClient = { topic: (name, schema) => { return { initiate: async () => { return ensureInitiated(name, schema); }, publish: async (json) => { await ensureInitiated(name, schema); if (_type) { const data = _type.toBuffer( json ); await _topic.publishMessage({ data }); } else { await _topic.publishMessage({ json }); } } }; } }; return typedClient; }; var schemaExists = async (client, schemaId) => { for await (const s of client.listSchemas()) { if (s.name?.endsWith(`/${schemaId}`)) { return true; } } return false; }; // src/lib/subscriber.ts import { PubSub as PubSub2 } from "@google-cloud/pubsub"; import { Type as Type2 } from "avsc"; var makeSureSubscriptionExists = async (topic, name, options) => { const createSubscriptionOptions = { messageRetentionDuration: { seconds: options?.messageRetentionDuration || 3600 * 24 // Default to 1 day. }, expirationPolicy: { ttl: { seconds: options?.expirationPolicy || 3600 * 24 * 7 // Default to 7 days. } } }; const [exists] = await topic.subscription(name).exists(); if (exists) { return; } await topic.createSubscription(name, createSubscriptionOptions); }; var createSubscriber = (clientOptions) => { const client = clientOptions ? new PubSub2(clientOptions) : new PubSub2(); let _type; const typedClient = { topic: (name, schema) => { if (schema && !_type) { const schemaType = Type2.forSchema(JSON.parse(schema.avroDefinition)); _type = schemaType; } const _topic = client.topic(name); return { initiate: async (subscriptionName, options) => { await makeSureSubscriptionExists(_topic, subscriptionName, options); }, subscribe: async (subscriptionName, callbacks, options) => { const subscription = _topic.subscription(subscriptionName); subscription.on("message", async (msg) => { const data = _type ? _type.fromBuffer(msg.data) : JSON.parse(msg.data.toString("utf8")); if (options?.autoAck === void 0 || options.autoAck === true) { try { await callbacks.onMessage(Object.assign(msg, { data })); msg.ack(); } catch (error) { msg.nack(); callbacks.onError ? callbacks.onError(Object.assign(msg, { data }), error) : console.error(error); } } else { await callbacks.onMessage(Object.assign(msg, { data })); } }); return subscription; }, close: async (subscriptionName) => { const subscription = _topic.subscription(subscriptionName); await subscription.close(); }, delete: async (subscriptionName) => { const subscription = _topic.subscription(subscriptionName); await subscription.delete(); } }; } }; return typedClient; }; export { createPublisher, createSubscriber };