UNPKG

node-test-bed-adapter

Version:

An adapter to connect a node.js application to the Test-bed's Common Information Space or Common Simulation Space.

181 lines 6.66 kB
import { existsSync, readdirSync, lstatSync } from 'fs'; import { default as axios } from 'axios'; import * as path from 'path'; import { Readable } from 'stream'; import { DataType } from 'test-bed-schemas'; import { LargeDataUpdateTopic } from '../avro/index.mjs'; import { Logger } from '../logger/logger.mjs'; export const clone = (model) => { return JSON.parse(JSON.stringify(model)); }; /** * Find all files recursively in specific folder with specific extension * * @param directoryName Path relative to this file or other file which requires this files * @param ext Extension name, e.g: '.html' * @return Result files with path string in an array */ export const findFilesInDir = (directoryName, ext) => { ext = (ext[0] === '.' ? ext : `.${ext}`).toLowerCase(); let results = []; if (!existsSync(directoryName)) { console.error(`Error - Directory ${directoryName} does not exist`); return []; } readdirSync(directoryName).forEach((f) => { const filename = path.join(directoryName, f); const stat = lstatSync(filename); if (stat.isDirectory()) { results = results.concat(findFilesInDir(filename, ext)); // recurse } else if (path.extname(filename).toLowerCase() === ext) { results.push(filename); } }); return results; }; /** * Look for schema files that represent a value, but without a corresponding key file, i.e. * a schema file names <topic>-value.avsc should also have a <topic>-key.avsc. * * @param files Found schema files * @return missing key schema files */ export const findMissingKeyFiles = (files) => { return files .filter((f) => path.basename(f).indexOf('-value.avsc') >= 0) .reduce((p, c) => { const keyFile = c.replace('-value.avsc', '-key.avsc'); if (files.filter((f) => f.indexOf(keyFile) >= 0).length > 0) { return p; } p.push(keyFile); return p; }, []); }; /** * Create a RFC4122 version 4 GUID * * @see https://stackoverflow.com/a/2117523/319711 * * @returns RFC4122 version 4 compliant GUID */ export const uuid4 = () => { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { // tslint:disable-next-line:no-bitwise const r = (Math.random() * 16) | 0; // tslint:disable-next-line:no-bitwise const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); }; /** * Check if the object is empty. * @param obj Any object * @see https://stackoverflow.com/a/32108184/319711 */ export const isEmptyObject = (obj) => Object.keys(obj).length === 0 && obj.constructor === Object; /** Convert a buffer to a readable stream, e.g. to send it via FormData */ export const bufferToStream = (buffer) => { const stream = new Readable(); stream.push(buffer); stream.push(null); return stream; }; /** * Helper function to create a callback that automatically sends a large file upload message * to the Test-bed's LargeDataUpdateTopic (system_large_data_update). This callback can be * passed to the uploadFile function of the adapter. * * @param adapter test bed adapter, needed to send the message * @param title title of the large file upload message * @param description description of the large file upload message * @param dataType data type of the message * @param callback to return the result of the large file upload (default logs errors) */ export const largeFileUploadCallback = (adapter, title, description, dataType = DataType.other, cb = (err) => err ? Logger.instance.error(err) : undefined) => { return (err, url) => { if (err) { return cb(err); } const msg = { url, title, description, dataType, }; const payload = { topic: LargeDataUpdateTopic, messages: [{ value: msg }], }; adapter.send(payload, cb); }; }; /** Is unique filter for array filter method */ export const isUnique = (value, index, arr) => arr.indexOf(value) === index; /** Check if the schema registry is available */ export const isSchemaRegistryAvailable = (options, log) => { const timeout = (options.retryTimeout || 5) * 1000; const maxRetries = options.maxConnectionRetries || 20; return new Promise((resolve, reject) => { let retries = maxRetries; const url = resolveUrl(options.schemaRegistry, 'subjects'); const intervalId = setInterval(() => { axios .get(url) .then(() => { log.info(`isSchemaRegistryAvailable - Accessed schema registry in ${maxRetries - retries}x.`); clearInterval(intervalId); resolve(); }) .catch(() => { retries--; log.warn(`isSchemaRegistryAvailable - Failed to access schema registry at ${url}. Retried ${maxRetries - retries}x.`); if (retries <= 0) { clearInterval(intervalId); log.error(`isSchemaRegistryAvailable - Cannot access schema registry at ${url}. Retried ${maxRetries}x. Giving up.`); reject('isSchemaRegistryAvailable - Schema registry is not available.'); } }); }, timeout); }); }; export const resolveUrl = (from, to) => { const resolvedUrl = new URL(to, new URL(from, 'resolve://')); if (resolvedUrl.protocol === 'resolve:') { // `from` is a relative URL. const { pathname, search, hash } = resolvedUrl; return pathname + search + hash; } return resolvedUrl.toString(); }; /** Sleep for msec time */ export const sleep = (msec) => new Promise((resolve) => setTimeout(resolve, msec)); Promise.map = async function (iterable, mapper, options = {}) { let { concurrency = Infinity } = options; let index = 0; const results = []; const pending = []; const iterator = iterable[Symbol.iterator](); while (concurrency-- > 0) { const thread = wrappedMapper(); if (thread) pending.push(thread); else break; } await Promise.all(pending); return results; function wrappedMapper() { const next = iterator.next(); if (next.done) return null; const i = index++; const mapped = mapper(next.value, i); return Promise.resolve(mapped).then((resolved) => { results[i] = resolved; return wrappedMapper(); }); } }; //# sourceMappingURL=index.mjs.map