UNPKG

semantic-network

Version:

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

506 lines 27.9 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; 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()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FieldResolverUtil = void 0; var semantic_link_1 = require("semantic-link"); var formTypes_1 = require("../types/formTypes"); var linkRelation_1 = require("../linkRelation"); var anylogger_1 = __importDefault(require("anylogger")); var formUtil_1 = require("./formUtil"); var resourceMergeFactory_1 = require("../representation/resourceMergeFactory"); var instanceOfCollection_1 = require("./instanceOf/instanceOfCollection"); var instanceOfDocumentRepresentationGroup_1 = require("./instanceOf/instanceOfDocumentRepresentationGroup"); var instanceOfDocumentRepresentation_1 = require("./instanceOf/instanceOfDocumentRepresentation"); var instanceOfUriListValue_1 = require("./instanceOf/instanceOfUriListValue"); var instanceOfSimpleValue_1 = require("./instanceOf/instanceOfSimpleValue"); var log = (0, anylogger_1.default)('FieldResolverUtil'); /** * An enumeration across the return type from a form field */ var FieldValueType; (function (FieldValueType) { /** * The value is single */ FieldValueType[FieldValueType["single"] = 0] = "single"; /** * The value is contained in an array * * Note: this closely aligns with the {@link FormItem.multiple} flag when set of the {@link FieldType.Select} */ FieldValueType[FieldValueType["multiple"] = 1] = "multiple"; /** * The value is contained in an object */ FieldValueType[FieldValueType["group"] = 2] = "group"; })(FieldValueType || (FieldValueType = {})); /** * Mapping strategy between the across-the-wire field types and internal types * * TODO: this needs to be injectable/extendable */ function formType(formItem) { switch (formItem.type) { case formTypes_1.FieldType.Select: return FieldValueType.multiple; case formTypes_1.FieldType.Group: case formTypes_1.FieldType.Collection: return FieldValueType.group; case formTypes_1.FieldType.Text: case formTypes_1.FieldType.TextArea: case formTypes_1.FieldType.Password: case formTypes_1.FieldType.Address: case formTypes_1.FieldType.Email: case formTypes_1.FieldType.Uri: case formTypes_1.FieldType.Currency: case formTypes_1.FieldType.Number: case formTypes_1.FieldType.Height: case formTypes_1.FieldType.Checkbox: case formTypes_1.FieldType.Date: case formTypes_1.FieldType.DateTime: case formTypes_1.FieldType.Tel: case formTypes_1.FieldType.EmailList: case formTypes_1.FieldType.Signature: case formTypes_1.FieldType.AddressPostal: default: return FieldValueType.single; } } var FieldResolverUtil = /** @class */ (function () { function FieldResolverUtil() { } /** * Recursive, resolving merger that adds fields to a document based on a (create or edit) form * and the allowed fields with their type. * * Note: field names are converted to tracked names */ FieldResolverUtil.resolveFields = function (document, form, options) { return __awaiter(this, void 0, void 0, function () { var defaultFields, fieldsToResolve, fieldsToResolve_1, fieldsToResolve_1_1, field, formItem, fieldValue, fieldResolver, e_1_1; var e_1, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: defaultFields = __assign({}, options).defaultFields; log.debug('resolving fields on \'%s\'', semantic_link_1.LinkUtil.getUri(document, linkRelation_1.LinkRelation.Self)); fieldsToResolve = formUtil_1.FormUtil.fieldsToResolve(document, form, defaultFields); _b.label = 1; case 1: _b.trys.push([1, 7, 8, 9]); fieldsToResolve_1 = __values(fieldsToResolve), fieldsToResolve_1_1 = fieldsToResolve_1.next(); _b.label = 2; case 2: if (!!fieldsToResolve_1_1.done) return [3 /*break*/, 6]; field = fieldsToResolve_1_1.value; formItem = formUtil_1.FormUtil.findByField(form, field); if (!formItem) return [3 /*break*/, 4]; return [4 /*yield*/, this.resolve(document[field], formItem, options)]; case 3: fieldValue = _b.sent(); if (fieldValue) { fieldResolver = __assign({}, options).fieldResolver; log.debug('resolving field \'%s\'', field); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore TS2538: Type 'Omit ' cannot be used as an index type. (document)[field] = fieldResolver ? fieldResolver(field, fieldValue, options) : fieldValue; } else { // an undefined result adds 'undefined' to field rather than remove // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore TS2538: Type 'Omit ' cannot be used as an index type. document[field] = fieldValue; // catering 'false' which would log this error (probably do !! on if) if (fieldValue === undefined || fieldValue === null) { log.warn('Field \'%s\' is not resolved', field); } } return [3 /*break*/, 5]; case 4: log.info('Field \'%s\' is not matched in form', field); _b.label = 5; case 5: fieldsToResolve_1_1 = fieldsToResolve_1.next(); return [3 /*break*/, 2]; case 6: return [3 /*break*/, 9]; case 7: e_1_1 = _b.sent(); e_1 = { error: e_1_1 }; return [3 /*break*/, 9]; case 8: try { if (fieldsToResolve_1_1 && !fieldsToResolve_1_1.done && (_a = fieldsToResolve_1.return)) _a.call(fieldsToResolve_1); } finally { if (e_1) throw e_1.error; } return [7 /*endfinally*/]; case 9: return [2 /*return*/, document]; } }); }); }; /** * Takes an incoming polymorphic field ({@link SimpleValue}, {@link UriListValue} or {@link ResourceValue}) and * checks if its value is valid against the form. To do this, it will also resolve fields if necessary. * * Resolutions are performed via injected {@link MergeOptions.resolver} and {@link MergeOptions.resourceResolver} * * @param fieldValue * @param item * @param options? */ FieldResolverUtil.resolve = function (fieldValue, item, options) { return __awaiter(this, void 0, void 0, function () { var _a, resolver, field, resource, uri; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = __assign({}, options).resolver, resolver = _a === void 0 ? resourceMergeFactory_1.noopResolver : _a; return [4 /*yield*/, this.resolveByType(fieldValue, item, options)]; case 1: field = _b.sent(); if (field && (0, instanceOfSimpleValue_1.instanceOfSimpleValue)(field)) { return [2 /*return*/, resolver.resolve(field)]; } else if ((0, instanceOfUriListValue_1.instanceOfUriListValue)(field)) { return [2 /*return*/, field.map(function (uri) { return resolver.resolve(uri); })]; } if (!(0, semantic_link_1.instanceOfLinkedRepresentation)(fieldValue)) return [3 /*break*/, 3]; return [4 /*yield*/, this.resolveResource(fieldValue, item, options)]; case 2: resource = _b.sent(); if (resource) { uri = semantic_link_1.LinkUtil.getUri(resource, linkRelation_1.LinkRelation.Self); if (uri) { return [2 /*return*/, resolver.resolve(uri)]; } } _b.label = 3; case 3: log.debug('Field type unknown - returning default value'); return [2 /*return*/, fieldValue]; } }); }); }; /** * Resolves any {@link FormItem.id} as a {@link LinkedRepresentation} when there is a {@link MergeOptions.resourceResolver}. * * Note: * - current strategy to resolve on the title of the Canonical on the field value. * - there is no placement of the resolution onto the item * * @param formItem form item to process for resolution * @param fieldValue * @param options */ FieldResolverUtil.resolveResource = function (fieldValue, formItem, options) { var _a; return __awaiter(this, void 0, void 0, function () { var _b, _c, resourceResolverRelNameStrategy, resourceResolver, relName, item; return __generator(this, function (_d) { switch (_d.label) { case 0: _b = __assign({}, options), _c = _b.resourceResolverRelNameStrategy, resourceResolverRelNameStrategy = _c === void 0 ? this.resourceResolverRelNameStrategy : _c, resourceResolver = _b.resourceResolver; relName = resourceResolverRelNameStrategy(fieldValue); if (!relName) return [3 /*break*/, 5]; item = (_a = formItem.items) === null || _a === void 0 ? void 0 : _a.find(function (x) { return x.name === relName; }); if (!item) return [3 /*break*/, 4]; if (!item.id) return [3 /*break*/, 3]; if (!resourceResolver) return [3 /*break*/, 2]; log.debug('matching items collection with resolver relName \'%s\' on %s', relName, formItem.id); return [4 /*yield*/, resourceResolver(relName)(fieldValue, options)]; case 1: return [2 /*return*/, _d.sent()]; case 2: return [3 /*break*/, 4]; case 3: log.debug('form item might expected \id\ attribute of resource'); _d.label = 4; case 4: return [3 /*break*/, 6]; case 5: log.bind('rel name strategy did not return name on %s', semantic_link_1.LinkUtil.getUri(fieldValue, linkRelation_1.LinkRelation.Self)); _d.label = 6; case 6: return [2 /*return*/, fieldValue]; } }); }); }; /** * * Strategy is to resolve the lookup key as the title from the Canonical link relation * @param resource field resource being resolved */ FieldResolverUtil.resourceResolverRelNameStrategy = function (resource) { return semantic_link_1.LinkUtil.getTitle(resource, linkRelation_1.LinkRelation.Canonical); }; /** * If a {@link MergeOptions.resolver} has been provided then there is potentially a pooled resource available * requiring resolution * * @param fieldValue * @param item * @param options contains the resolver to be used */ FieldResolverUtil.resolveByPooled = function (fieldValue, item, options) { return __awaiter(this, void 0, void 0, function () { var _a, resolver, resource, uri; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = __assign({}, options).resolver, resolver = _a === void 0 ? resourceMergeFactory_1.noopResolver : _a; if (!(0, instanceOfSimpleValue_1.instanceOfSimpleValue)(fieldValue)) return [3 /*break*/, 1]; return [2 /*return*/, resolver.resolve(fieldValue)]; case 1: if (!(0, instanceOfUriListValue_1.instanceOfUriListValue)(fieldValue)) return [3 /*break*/, 2]; return [2 /*return*/, fieldValue.map(function (uri) { return resolver.resolve(uri); })]; case 2: if (!(0, semantic_link_1.instanceOfLinkedRepresentation)(fieldValue)) return [3 /*break*/, 5]; return [4 /*yield*/, this.resolveResource(fieldValue, item, options)]; case 3: resource = _b.sent(); if (!resource) return [3 /*break*/, 5]; uri = semantic_link_1.LinkUtil.getUri(resource, linkRelation_1.LinkRelation.Self); if (!uri) return [3 /*break*/, 5]; return [4 /*yield*/, this.resolveByPooled(uri, item, options)]; case 4: return [2 /*return*/, _b.sent()]; case 5: log.debug('Field type unknown - returning default value'); return [2 /*return*/, fieldValue]; } }); }); }; /** * * @param fieldValue * @param formItem * @param options */ FieldResolverUtil.resolveByType = function (fieldValue, formItem, options) { return __awaiter(this, void 0, void 0, function () { var _a, value, resourceResolver; var _this = this; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = formType(formItem); switch (_a) { case FieldValueType.single: return [3 /*break*/, 1]; case FieldValueType.multiple: return [3 /*break*/, 2]; case FieldValueType.group: return [3 /*break*/, 7]; } return [3 /*break*/, 14]; case 1: if (fieldValue && !(0, instanceOfSimpleValue_1.instanceOfSimpleValue)(fieldValue) && !(typeof fieldValue === 'object')) { log.warn('Unexpected type \'%s\' on form type %s with \'%s\'', typeof fieldValue, formItem.type, formItem.name); return [2 /*return*/, undefined]; } if (formItem.multiple) { log.warn('Unexpected attribute \'multiple\' on form type %s with \'%s\'', formItem.type, formItem.name); } return [2 /*return*/, fieldValue]; case 2: return [4 /*yield*/, formUtil_1.FormUtil.resolveItemsFromCollection(formItem, options)]; case 3: _b.sent(); return [4 /*yield*/, this.findValueInItems(formItem, fieldValue, options)]; case 4: value = _b.sent(); if (!(!value && (0, instanceOfDocumentRepresentation_1.instanceOfDocumentRepresentation)(fieldValue))) return [3 /*break*/, 6]; resourceResolver = __assign({}, options).resourceResolver; if (!resourceResolver) return [3 /*break*/, 6]; return [4 /*yield*/, resourceResolver(formItem.name)(fieldValue, options)]; case 5: value = _b.sent(); _b.label = 6; case 6: // check the value returned needs to be an enumeration. if (formItem.multiple) { // return back an Uri[] removing all undefined to meet {@link UriListValue} return [2 /*return*/, ((0, instanceOfSimpleValue_1.instanceOfSimpleValue)(value) ? [value] : value) .filter(function (x) { return !!x; })]; // check that the value is part of the provided items enumeration in the form } else { // undefined is acceptable as it is {@link SimpleValue} return [2 /*return*/, value]; } _b.label = 7; case 7: if (!formItem.multiple) return [3 /*break*/, 11]; if (!(0, instanceOfDocumentRepresentationGroup_1.instanceOfDocumentRepresentationGroup)(fieldValue)) return [3 /*break*/, 9]; return [4 /*yield*/, Promise.all(fieldValue.map(function (value) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!value) return [3 /*break*/, 2]; return [4 /*yield*/, this.resolveFields(value, formItem, options)]; case 1: return [2 /*return*/, _a.sent()]; case 2: return [2 /*return*/]; } }); }); }))]; case 8: return [2 /*return*/, (_b.sent()) // remove any undefined .filter(function (x) { return !!x; })]; case 9: log.error('Group with multiple must be implemented as an array of Document Representation'); _b.label = 10; case 10: return [3 /*break*/, 13]; case 11: if (!(0, instanceOfDocumentRepresentation_1.instanceOfDocumentRepresentation)(fieldValue)) { log.warn('Group must be implemented a DocumentRepresentation'); } return [4 /*yield*/, this.resolveFields(fieldValue, formItem, options)]; case 12: // otherwise it is a single group and resolve treating the object as a LinkedRepresentation // throw new Error('Group not implemented'); return [2 /*return*/, _b.sent()]; case 13: return [3 /*break*/, 15]; case 14: log.warn('Unknown form type \'%s\' on \'%s\'', formItem.type, formItem.name); return [2 /*return*/, undefined]; case 15: return [2 /*return*/]; } }); }); }; /** * Matches a field value against the form items switching across {@link FieldValue} type. * * - {@link SimpleValue} - look through the list by value * - {@link UriListValue} - look across the list by value * - {@link ResourceValue} - look into the resource's {@link LinkRelation.Canonical} to locate the 'title' * * @param formItem a form item (with items eager loaded) * @param fieldValue * @param options */ FieldResolverUtil.findValueInItems = function (formItem, fieldValue, options) { var _a; return __awaiter(this, void 0, void 0, function () { var uri_1; var _this = this; return __generator(this, function (_b) { switch (_b.label) { case 0: if (!(fieldValue && (formItem === null || formItem === void 0 ? void 0 : formItem.items))) return [3 /*break*/, 5]; if (!(0, instanceOfSimpleValue_1.instanceOfSimpleValue)(fieldValue)) return [3 /*break*/, 1]; if (formItem.items.some(function (item) { return item.value === fieldValue; })) { return [2 /*return*/, fieldValue]; } return [3 /*break*/, 4]; case 1: if (!(0, instanceOfUriListValue_1.instanceOfUriListValue)(fieldValue)) return [3 /*break*/, 3]; return [4 /*yield*/, Promise.all(fieldValue.map(function (val) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.findValueInItems(formItem, val, options)]; case 1: return [2 /*return*/, _a.sent()]; } }); }); }))]; case 2: return [2 /*return*/, _b.sent()]; case 3: if ((0, instanceOfDocumentRepresentation_1.instanceOfDocumentRepresentation)(fieldValue)) { // a lazy-loaded set of items is currently loaded from a collection if ((0, instanceOfCollection_1.instanceOfCollection)(formItem.items)) { if ((0, semantic_link_1.instanceOfLinkedRepresentation)(fieldValue)) { uri_1 = semantic_link_1.LinkUtil.getUri(fieldValue, linkRelation_1.LinkRelation.Self); if (uri_1) { if (formItem.items.items.some(function (item) { return semantic_link_1.LinkUtil.getUri(item, linkRelation_1.LinkRelation.Self) === uri_1; })) { return [2 /*return*/, uri_1]; // return type Uri } // else fall through to return undefined } else { log.warn('Field value not a linked representation'); } } // else fall through to return undefined } else { if (Array.isArray(formItem.items)) { log.debug('Form items already loaded or recursively nothing to load: count \'%s\'', (_a = formItem.items) === null || _a === void 0 ? void 0 : _a.length); } else { log.warn('Form items type not found \'%s\'', typeof fieldValue); } // else fall through to return undefined } } else { log.error('Field value type not found \'%s\'', typeof fieldValue); // else fall through to return undefined } _b.label = 4; case 4: return [3 /*break*/, 6]; case 5: log.warn('No items on form \'%s\'', formItem.name); _b.label = 6; case 6: log.debug('Value \'%s\' is not found on \'%s\' - still allowing value', fieldValue, formItem.name); return [2 /*return*/, undefined]; } }); }); }; return FieldResolverUtil; }()); exports.FieldResolverUtil = FieldResolverUtil; //# sourceMappingURL=fieldResolverUtil.js.map