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.

78 lines
2.9 KiB
JavaScript

"use strict";
const Promise = require("bluebird");
const syncpipe = require("syncpipe");
const util = require("util");
const Result = require("../result");
const ID = Symbol("dlayer-source object ID");
const AllowErrors = Symbol("dlayer-source allow-errors marker");
// TODO: Make more readable
module.exports = {
withSources: function withSources(schemaObject) {
let { $sources, ... rest } = schemaObject;
let generatedProperties = syncpipe($sources ?? {}, [
(_) => Object.entries(_),
(_) => _.flatMap(([ source, properties ]) => {
return Object.entries(properties).map(([ property, selector ]) => {
let getter = function (_args, context) {
return Promise.try(() => {
if (properties[ID] != null) {
let dataSource = context.sources[source];
if (dataSource != null) {
// console.log(`Calling source '${source}' with ID ${util.inspect(properties[ID])}`);
return Result.wrapAsync(() => dataSource.load(properties[ID]));
} else {
throw new Error(`Attempted to read from source '${source}', but no such source is registered`);
}
} else {
// FIXME: Better error message
throw new Error(`Must specify a dlayer-source ID`);
}
}).then((result) => {
// console.log(`Result [${source}|${util.inspect(properties[ID])}] ${util.inspect(result)}`);
// TODO: How to deal with null results? Allow them or not? Make it an option?
if (result.isError) {
if (properties[AllowErrors] === true) {
// This 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).
return (typeof selector === "string")
? undefined
: selector(result);
} else {
// This is equivalent to a `throw`, and so we just propagate it
return result;
}
} else if (result.value() != null) {
// This is to support property name shorthand used in place of a selector function
return (typeof selector === "string")
? result.value()[selector]
: selector(result.value());
} else {
throw new Error(`Null-ish result returned for ID ${util.inspect(properties[ID])} from source '${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!`);
}
});
};
return [ property, getter ];
});
}),
(_) => Object.fromEntries(_)
]);
// NOTE: We always specify the generated properties first, so that properties can be overridden by explicit known values to bypass the source lookup, if needed by the implementation
return {
... generatedProperties,
... rest
};
},
ID: ID,
AllowErrors: AllowErrors
};