data-interchange
Version:
Data interchange handler & DB fallback handler
173 lines (172 loc) • 8.35 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createInterchange = exports.READ_RESULT_DEFAULT = void 0;
const layerr_1 = require("layerr");
const promise_1 = require("./promise");
const boomerang_1 = require("./boomerang");
const queue_1 = require("./queue");
const types_1 = require("./types");
exports.READ_RESULT_DEFAULT = (res) => {
if (typeof res === UNDEFINED || res === null) {
return types_1.ReadAction.Fallback;
}
return types_1.ReadAction.Return;
};
const UNDEFINED = "undefined";
const VALUE_NOOP = (val) => val;
function createInterchange(sources, options = {}) {
options.queue = options.queue || queue_1.createQueue();
return {
read: (id) => processRead(sources, id, options),
write: (value) => processWrite(sources, value, options)
};
}
exports.createInterchange = createInterchange;
function processRead(sources, id, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
let lastValue, lastConverters = null;
yield boomerang_1.boomerang(sources, (source, sourceIndex, returning) => __awaiter(this, void 0, void 0, function* () {
const { read = null, readError = () => types_1.ReadAction.Throw, readResult = exports.READ_RESULT_DEFAULT, write = null, writeMissingRead = true } = source;
// Prepare value-conversion functions
let convertRead = VALUE_NOOP, convertWrite = VALUE_NOOP;
if (typeof source.convert === "object") {
const { read: tempRead, write: tempWrite } = (source).convert;
convertRead = tempRead || convertRead;
convertWrite = tempWrite || convertWrite;
}
// Check if rolling back up the chain (boomerang)
if (returning) {
const [returnConvertRead] = lastConverters;
// Convert value to next item format UP the chain
lastValue = returnConvertRead(lastValue);
lastConverters = [convertRead, convertWrite];
// Because all prior indexes (sources) did NOT
// return a value, they should be written to
if (writeMissingRead && typeof write === "function") {
let writeResult = source.queueWriteKey
? queue_1.enqueue(options.queue, resolveQueueKey(lastValue, source.queueWriteKey), () => write(lastValue))
: write(lastValue);
if (promise_1.isPromise(writeResult)) {
writeResult = yield writeResult;
}
lastValue = typeof writeResult === UNDEFINED ? lastValue : writeResult;
}
return;
}
// Attempt to read
if (typeof read === "function") {
let initialValue;
try {
initialValue = source.queueReadKey
? queue_1.enqueue(options.queue, resolveQueueKey(id, source.queueReadKey), () => read(id))
: read(id);
if (promise_1.isPromise(initialValue)) {
initialValue = yield initialValue;
}
}
catch (err) {
const action = readError(err);
if (action === types_1.ReadAction.Throw) {
throw new layerr_1.Layerr(err, "Interchange read error");
}
else if (action === types_1.ReadAction.Fallback && sourceIndex === sources.length - 1) {
throw new layerr_1.Layerr(err, "Interchange fallback requested, but none available");
}
else if (action === types_1.ReadAction.Return) {
return lastValue; // Probably undefined
}
}
lastValue = initialValue;
lastConverters = [convertRead, convertWrite];
const readValueAction = readResult(lastValue);
if (readValueAction === types_1.ReadAction.Return) {
return boomerang_1.BOOMERANG_RETURN;
}
else if (readValueAction === types_1.ReadAction.Throw) {
throw new layerr_1.Layerr(`readResult read handler flagged result as invalid for source ${sourceIndex}`);
}
// ReadAction.Fallback simply loops
}
}));
return lastValue;
});
}
function resolveQueueKey(value, resolver) {
return typeof resolver === "string" ? resolver : resolver(value);
}
function processWrite(sources, value, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
const { writeMode = types_1.WriteMode.Series } = options;
if (writeMode === types_1.WriteMode.Parallel) {
// All sources are written with the same value in parallel
yield Promise.all(sources.map((source) => __awaiter(this, void 0, void 0, function* () {
const { writeWait = true } = source;
if (typeof source.write !== "function")
return;
const result = source.queueWriteKey
? queue_1.enqueue(options.queue, resolveQueueKey(value, source.queueWriteKey), () => source.write(value))
: source.write(value);
if (writeWait && promise_1.isPromise(result)) {
yield result;
}
})));
return value;
}
else if (writeMode === types_1.WriteMode.Series) {
// Run from first to last, converting, and then backwards
// writing each value
let lastValue = value;
const values = sources.map((source, ind) => {
if (ind === 0)
return value;
const { convert = {} } = source;
const { write = VALUE_NOOP } = convert;
lastValue = write(lastValue);
return lastValue;
});
// Run backwards writing
lastValue = values[values.length - 1];
for (let ind = sources.length - 1; ind >= 0; ind -= 1) {
const { queueWriteKey, write, writeWait = true } = sources[ind];
if (typeof write !== "function")
continue;
const writeResult = queueWriteKey
? queue_1.enqueue(options.queue, resolveQueueKey(lastValue, queueWriteKey), () => write(lastValue))
: write(lastValue);
if (writeWait) {
lastValue = promise_1.isPromise(writeResult) ? yield writeResult : writeResult;
if (ind > 0) {
const { convert = {} } = sources[ind];
const { read = VALUE_NOOP } = convert;
// Convert this value
lastValue = read(lastValue);
}
}
else {
// Not waiting, so set the lastValue to be the
// correct pre-calculated value
if (ind > 0) {
lastValue = values[ind - 1];
}
else {
// First index
lastValue = values[0];
}
}
}
return lastValue;
}
else {
throw new layerr_1.Layerr(`Invalid write mode: ${writeMode}`);
}
});
}