UNPKG

semantic-network

Version:

A utility library for manipulating a list of links that form a semantic interface to a network of resources.

219 lines 7.65 kB
import { __awaiter } from "tslib"; import { LinkUtil } from 'semantic-link'; import { LinkRelation } from '../linkRelation'; import anylogger from 'anylogger'; import { FormUtil } from '../utils/formUtil'; import { FieldResolverUtil } from '../utils/fieldResolverUtil'; import { FieldLinksResolverUtil } from '../utils/fieldLinksResolverUtil'; export const noop = () => { }; export const noopResolver = { resolve: _ => _, remove: noop, add: noop, update: noop, }; export const noopResourceResolver = () => () => __awaiter(void 0, void 0, void 0, function* () { return undefined; }); const log = anylogger('ResourceMerger'); /** * Processes difference sets (created, update, delete) for between two client-side collections {@Link CollectionRepresentation} * */ export class ResourceMergeFactory { static createMerge(resource, form, options) { return __awaiter(this, void 0, void 0, function* () { return yield this.merge(resource, form, options); }); } /** * A three-way merge between a form resource and existing {@link LinkedRepresentation} * client-side representation within the api network of data and a new {@Link LinkedRepresentation} document. * * Use option {@link EditMergeOptions.undefinedWhenNoUpdateRequired} to return undefined * when no update is required * * The basic merge is: * 1. remove any fields document representation that are not field items in the form * 2. merge the document into the client-side representation * @param resource a clean over-the-wire version * @param document any updates * @param form required to specify the merge fields * @param options */ static editMerge(resource, document, form, options) { return __awaiter(this, void 0, void 0, function* () { const { undefinedWhenNoUpdateRequired = true, defaultFields, } = Object.assign({}, options); const newDocument = yield this.merge(document, form, options); // return undefined if the flag is on and there are no updates if (undefinedWhenNoUpdateRequired) { // now check if the two resources are actually different based on matching only fields that need to be returned const fieldsToUpdate = FormUtil.fieldsRequiringUpdate(resource, newDocument, form, defaultFields); if ((fieldsToUpdate === null || fieldsToUpdate === void 0 ? void 0 : fieldsToUpdate.length) > 0) { log.debug('Update required on \'%s\': [different fields \'%s\']', newDocument.name || LinkUtil.getUri(document, LinkRelation.Self), fieldsToUpdate.join(',')); return newDocument; } else { return undefined; } } else { return newDocument; } }); } /** * Makes the new document with all links and fields resolved. * @param document * @param form * @param {MergeOptions} options? * @return {Promise.<*>|Promise} containing the document updates to be merged * @private */ static resolveLinksAndFields(document, form, options) { return __awaiter(this, void 0, void 0, function* () { // step : merge links relations into fields const resolvedDocument = yield FieldResolverUtil.resolveFields(document, form, options); const resolvedLinks = yield FieldLinksResolverUtil.resolveLinks(document, form, options); // step : merge all fields together as a document return Object.assign(Object.assign({}, resolvedDocument), resolvedLinks); }); } /** * A merge between the a form resource and an existing resource. It merges based on * both attributes and link relations in a resource * * Example One: * * form fields: * 'name', 'job', 'relates' * * resource: * { * links: [ * { rel: 'Self', href: 'http://example.com/item/1' }, * { rel: 'relates', href: 'http://example.com/job/1' }, * ], * name: 'this', * job: 'that' * type: '1' * } * * result: * { * relates: 'http://example.com/job/1', * name: 'this', * job: 'that' * } * * * The resolver will match against fields and return a value. This is used * for example with the 'relates' attribute to return a href reference to the parent resource * * Example Two: '//types/form/collection' * * form fields: * { * * "links": [ * { * "rel": "Self", * "href": "http://localhost:1080/page/form/edit" * } * ], * "items": [ * { * "type": "//types/form/text", * "name": "title", * "description": "The title of the survey" * }, * { * "type": "//types/form/collection", * "name": "role", * "description": "An optional list of roles to be granted access to the page" * } * ] *} * * resource: * { * links: [ * { rel: 'Self', href: 'http://example.com/item/1' }, * { rel: 'role', href: 'http://example.com/role/1' }, * { rel: 'role', href: 'http://example.com/role/2' }, * ], * title: 'this', * } * * result: * { * role: ['http://example.com/role/1', 'http://example.com/role2'] * name: 'this', * } * Example Three: '//types/form/group' * * form fields: * { * * "links": [ * { * "rel": "Self", * "href": "http://localhost:1080/page/form/edit" * } * ], * "items": [ * { * "type": "//types/form/text", * "name": "title", * "description": "The title of the survey" * }, * { * * "type": "//types/form/group", * "name": "textBox", * "items": [ * { * "type": "//types/form/text", * "name": "height", * "description": "The height of the text box in lines" * }, * { * "type": "//types/form/text", * "name": "width", * "description": "The width of the text box in characters" * } * ], * "description": "Dimensions for a text box" * } * ] *} * resource: * { * links: [ * { rel: 'Self', href: 'http://example.com/item/1' }, * ], * textBox: { * height: 5, * width: 20 * } * } * * result: * { * textBox: { * height: 5, * width: 20 * } * } * @param resource * @param form * @param options * @return the resource to be created */ static merge(resource, form, options) { return __awaiter(this, void 0, void 0, function* () { const resolvedDocument = yield this.resolveLinksAndFields(resource, form, options); return FormUtil.fieldsToReturnFromForm(resolvedDocument, form, options); }); } } //# sourceMappingURL=resourceMergeFactory.js.map