@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
JavaScript
// 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
};