You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

66 lines
2.7 KiB
JavaScript

"use strict";
const util = require("util");
const Result = require("@joepie91/result");
const mapObject = require("map-obj");
const ID = Symbol("dlayer-source object ID");
const AllowErrors = Symbol("dlayer-source allow-errors marker");
// TODO: Make more readable
// TODO: Refactor allowErrors logic so that it's actually part of the internal $getProperty implementation in dlayer itself, and this abstraction uses that tool?
module.exports = function dlayerSource(source, properties) {
// contextName, { targetProperty: sourceProperty }
return mapObject(properties, (property, selector) => {
return [
property,
async function (_args, context) {
let dataSource = context[source];
let sourceID = properties[ID];
let allowErrors = (properties[AllowErrors] === true);
let isCustomSelector = (typeof selector !== "string");
if (dataSource == null) {
throw new Error(`Attempted to read from context property '${source}', but no such property exists`);
} else if (sourceID == null) {
// FIXME: Better error message
throw new Error(`Must specify a dlayer-source ID`);
} else {
let result = await Result.wrapAsync(() => dataSource.load(sourceID));
// The AllowErrors option is set when a source definition has its own way to deal with (allowable) errors. Instead of simply propagating the error for all affected attributes, it calls the attribute handlers with the Result (or returns `undefined` if only a property is specified).
// TODO: How to deal with null results? Allow them or not? Make it an option?
if (result.isOK && result.value() == null) {
// TODO: Change implementation to allow `Result.ok(null|undefined)` but not `null|undefined` directly?
throw new Error(`Null-ish result returned for ID ${util.inspect(properties[ID])} from source at context property '${source}'; this is not allowed, and there is probably a bug in your code. Please file a ticket if you have a good usecase for null-ish results!`);
} else if (allowErrors === true && isCustomSelector) {
// Custom selectors always receive the Result as-is (note that this has to come before the error case handling!)
return selector(result, context);
} else if (result.isError) {
if (allowErrors === true) {
// TODO: Does this actually make sense?
return undefined;
} else {
// This is equivalent to a `throw`, and so we just propagate it
return result;
}
} else if (isCustomSelector) {
return selector(result.value(), context);
} else {
// This is to support property name shorthand used in place of a selector function
return result.value()[selector];
}
}
}
];
});
};
Object.assign(module.exports, {
ID: ID,
AllowErrors: AllowErrors
});