@aimee-blue/ab-service-kit
Version:
Aimee Blue Service Template
177 lines (141 loc) • 5.85 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.prepareTopics = prepareTopics;
exports.publish = publish;
exports.subscribe = subscribe;
exports.getTopic = exports.pubsubClient = void 0;
var PubSub = _interopRequireWildcard(require("@google-cloud/pubsub"));
var _rxjs = require("rxjs");
var _operators = require("rxjs/operators");
var _app = require("../app");
var _os = require("os");
var _isTest = require("../isTest");
var _registerError = require("../registerError");
var _logging = require("../logging");
var _uuid = require("../uuid");
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
let initializedClient = null;
const pubsubClient = () => {
return initializedClient || (initializedClient = new PubSub.PubSub());
};
exports.pubsubClient = pubsubClient;
const topicMap = new Map();
const retrySettings = {
retryCodes: [10, // 'ABORTED'
1, // 'CANCELLED',
4, // 'DEADLINE_EXCEEDED'
13, // 'INTERNAL'
8, // 'RESOURCE_EXHAUSTED'
14, // 'UNAVAILABLE'
2 // 'UNKNOWN'
],
backoffSettings: {
initialRetryDelayMillis: 5,
retryDelayMultiplier: 2,
maxRetryDelayMillis: 60000,
initialRpcTimeoutMillis: 10000,
rpcTimeoutMultiplier: 1.0,
maxRpcTimeoutMillis: 10000,
totalTimeoutMillis: 10000
}
};
const addTopicToMap = topic => {
const topicWithOpts = pubsubClient().topic(topic);
topicWithOpts.setPublishOptions({
batching: {
maxMessages: 1,
maxMilliseconds: 1,
maxBytes: 1
},
gaxOpts: {
retry: retrySettings
}
});
topicMap.set(topic, topicWithOpts);
return topicMap.get(topic);
};
const getTopic = topic => {
const topicPublisher = topicMap.has(topic) ? topicMap.get(topic) : addTopicToMap(topic);
if (!topicPublisher) {
return addTopicToMap(topic);
}
return topicPublisher;
};
exports.getTopic = getTopic;
async function prepareTopics(topics) {
topics.forEach(topic => {
addTopicToMap(topic);
});
}
const TIMEOUT_ERROR = 'Retry total timeout exceeded before any response';
async function publish(topic, data, logger = (0, _logging.defaultBasicLogger)()) {
const topicPublisher = getTopic(topic);
topicPublisher.publish(Buffer.from(JSON.stringify(data), 'utf8'), (err, mesId) => {
if (err) {
(0, _registerError.registerError)(err);
if (err.message.includes(TIMEOUT_ERROR)) {
topicMap.delete(topic);
}
logger.error(`💥 Error when publishing to topic ${topic} ${mesId ? `with message ${mesId}` : ''}`, err);
}
});
}
const subscriptionNamesByTopic = new Map();
async function createTopicAndSubscription(topic, fullName, options) {
const shortName = fullName.replace('@aimee-blue/', '');
const genName = (0, _isTest.isDevBuild)() ? `${shortName}-${String(process.env.USER)}` : `${shortName}-${(0, _uuid.uuid)()}`;
const lastName = subscriptionNamesByTopic.get(topic);
const {
autoCreateTopic,
subscriptionName,
autoCreateSubscription,
subscriptionOptions,
...subOpts
} = {
autoCreateTopic: true,
subscriptionName: lastName || genName,
autoCreateSubscription: true,
subscriptionOptions: undefined,
...options
};
const topicPublisher = getTopic(topic);
if (autoCreateTopic && !lastName) {
const [topicExists] = await topicPublisher.exists();
if (!topicExists) {
await topicPublisher.create();
}
}
const subscription = topicPublisher.subscription(subscriptionName, subOpts);
if (autoCreateSubscription && !lastName) {
const [exists] = await subscription.exists();
if (!exists) {
await subscription.create(subscriptionOptions);
}
} // if success, then remember:
if (!subscriptionNamesByTopic.get(topic)) {
subscriptionNamesByTopic.set(topic, subscriptionName);
}
return subscription;
}
function subscribe(topic, options, optLogger) {
return (0, _rxjs.defer)(() => (0, _rxjs.from)((0, _app.appName)())).pipe((0, _operators.switchMap)(name => createTopicAndSubscription(topic, name, options)), (0, _operators.switchMap)(subscription => new _rxjs.Observable(subscriber => {
const name = subscription.name;
const logger = optLogger || (0, _logging.defaultBasicLogger)();
logger.log(`${_os.EOL}🎬 Subscribing to topic "${topic}" with subscription "${name}"`, _os.EOL);
subscriber.add((0, _rxjs.merge)((0, _rxjs.fromEvent)(subscription, 'message'), (0, _rxjs.fromEvent)(subscription, 'error').pipe((0, _operators.map)(err => {
throw err;
}), (0, _operators.ignoreElements)())).subscribe(subscriber));
subscriber.add(() => {
subscription.close().then(() => {
logger.log(`${_os.EOL}🏁 Unsubscribed from "${subscription.name}"`, _os.EOL);
}).catch(err => {
(0, _registerError.registerError)(err);
logger.error(`${_os.EOL}💥 Error when unsubscribing from "${subscription.name}"`, err, _os.EOL);
});
});
})));
}
//# sourceMappingURL=pubsub.js.map