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.
127 lines
3.4 KiB
JavaScript
127 lines
3.4 KiB
JavaScript
"use strict";
|
|
|
|
const Promise = require("bluebird");
|
|
const objectFromEntries = require("object.fromentries");
|
|
const util = require("util");
|
|
|
|
function resolveFromDataSource(dataContext, dataSource, id) {
|
|
if (dataContext[dataSource] != null) {
|
|
return dataContext[dataSource].load(id);
|
|
} else {
|
|
throw new Error(`Specified data source '${dataSource}' does not exist`);
|
|
}
|
|
}
|
|
|
|
function withProperty(dataSource, id, property) {
|
|
return withData(dataSource, id, (value) => {
|
|
return value[property];
|
|
});
|
|
}
|
|
|
|
function withData(dataSource, id, callback) {
|
|
return function (args, context) {
|
|
let { data } = context;
|
|
|
|
return Promise.try(() => {
|
|
return resolveFromDataSource(data, dataSource, id);
|
|
}).then((value) => {
|
|
if (value != null) {
|
|
// FIXME: Inject 'properties'
|
|
return callback(value, args, context);
|
|
} else {
|
|
// QUESTION: Why do we disallow this again?
|
|
throw new Error(`Got a null-ish value from data source '${dataSource}' for ID '${util.inspect(id)}'`);
|
|
}
|
|
});
|
|
};
|
|
}
|
|
|
|
function withDynamicHandler(handler, object) {
|
|
return function (args, context) {
|
|
let { data } = context;
|
|
|
|
function resolveProperty(property, fromObject = object) {
|
|
if (typeof fromObject[property] !== "function") {
|
|
throw new Error(`FIXME: Properties can apparently be non-functions`);
|
|
}
|
|
|
|
return fromObject[property](args, context);
|
|
}
|
|
|
|
let extendedContext = {
|
|
... context,
|
|
resolveProperty: resolveProperty,
|
|
resolveProperties: function (properties, fromObject) {
|
|
return Promise.map(properties, (property) => {
|
|
return Promise.try(() => {
|
|
return resolveProperty(property, fromObject);
|
|
}).then((value) => {
|
|
return [ property, value ];
|
|
});
|
|
}).then((entries) => {
|
|
return objectFromEntries(entries);
|
|
});
|
|
},
|
|
resolvePropertyPath: function (propertyPath, fromObject) {
|
|
let initialObject = fromObject ?? object;
|
|
|
|
return Promise.reduce(propertyPath, (last, property) => {
|
|
if (last != null) {
|
|
return resolveProperty(property, last);
|
|
}
|
|
}, initialObject);
|
|
},
|
|
resolveDataSource: function (dataSource, id) {
|
|
return resolveFromDataSource(data, dataSource, id);
|
|
}
|
|
};
|
|
|
|
return handler(args, extendedContext);
|
|
};
|
|
}
|
|
|
|
let ID = Symbol("ID");
|
|
let LocalProperties = Symbol("LocalProperties");
|
|
let Dynamic = Symbol("Dynamic");
|
|
|
|
module.exports = {
|
|
ID: ID,
|
|
Dynamic: Dynamic,
|
|
LocalProperties: LocalProperties,
|
|
createDataObject: function createDataObject(mappings) {
|
|
let object = {};
|
|
|
|
if (mappings[LocalProperties] != null) {
|
|
Object.assign(object, mappings[LocalProperties]);
|
|
}
|
|
|
|
if (mappings[Dynamic] != null) {
|
|
for (let [property, handler] of Object.entries(mappings[Dynamic])) {
|
|
object[property] = withDynamicHandler(handler, object);
|
|
}
|
|
}
|
|
|
|
for (let [dataSource, items] of Object.entries(mappings)) {
|
|
if (items[ID] != null) {
|
|
let id = items[ID];
|
|
|
|
for (let [property, source] of Object.entries(items)) {
|
|
if (object[property] == null) {
|
|
if (typeof source === "string") {
|
|
object[property] = withProperty(dataSource, id, source);
|
|
} else if (typeof source === "function") {
|
|
object[property] = withData(dataSource, id, source);
|
|
} /* FIXME: else */
|
|
} else {
|
|
throw new Error(`Handler already defined for property '${property}' - maybe you specified it twice for different data sources?`);
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error(`No object ID was provided for the '${dataSource}' data source`);
|
|
}
|
|
}
|
|
|
|
return object;
|
|
}
|
|
};
|