UNPKG

semantic-network

Version:

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

156 lines 7.15 kB
import { instanceOfLinkedRepresentation, instanceOfLinkSelector, LinkUtil, } from 'semantic-link'; import { SparseRepresentationFactory } from '../representation/sparseRepresentationFactory'; import { LinkRelation } from '../linkRelation'; import anylogger from 'anylogger'; import { NamedRepresentationFactory } from '../representation/namedRepresentationFactory'; import { instanceOfCollection } from './instanceOf/instanceOfCollection'; const log = anylogger('RepresentationUtil'); export class RepresentationUtil { /** * Return the list of keys as a typed array from a representation. * * see https://fettblog.eu/typescript-better-object-keys/ * see https://stackoverflow.com/questions/52856496/typescript-object-keys-return-string * * @param representation representation object * @returns array of all the field property keys */ static properties(representation) { return Object.keys(representation) .filter(x => x !== 'links'); } static getProperty(o, propertyName) { return o[propertyName]; } /** * Finds (by OR) a resource item in a collection identified through a found link relation or resource attribute * that matches an item in the collection items. * * It looks for items: * * 1. matching link relation (default: Self) by uri * 2. field attribute (default: name ({@link TrackedRepresentationFactory.mappedTitleAttributeName}) on a resource by string * 3. link selector * */ static findInCollection(collection, options) { if (!collection || !instanceOfCollection(collection)) { log.error(`find resource in collection: failed — not an instance of collection '${LinkUtil.getUri(collection, LinkRelation.Self)}'`); return undefined; } const { rel = undefined, where = undefined } = Object.assign({}, options); let resourceIdentifier; if (typeof where === 'string' /* Uri */) { // treat predicate as Uri resourceIdentifier = where; } else if (instanceOfLinkedRepresentation(where)) { const uri = LinkUtil.getUri(where, rel || LinkRelation.Self); if (uri) { resourceIdentifier = uri; } else { log.debug('find resource in collection: not found — no \'where\' and \'rel\' options that combine to create resource identifier on \'%s\'', rel || LinkRelation.Self, LinkUtil.getUri(collection, LinkRelation.Self)); return undefined; } } else if (instanceOfLinkSelector(where)) { const uri = LinkUtil.getUri(collection, where); if (uri) { resourceIdentifier = uri; } else { log.debug('find resource in collection: not found — no \'where\' and link selector \'%o\' options that combine to create resource identifier on \'%s\'', where, LinkUtil.getUri(collection, LinkRelation.Self)); return undefined; } } else if (Array.isArray(where)) { log.debug('find resource in collection: array cannot be assigned to where'); } else if (!where) { log.debug('find resource in collection: where not used as \'%s\'', where); } else { log.debug('find resource in collection: unknown where'); } // attribute look up strategy. Used for fallback strategy 2. // TODO: allow for multiple link relations in underlying function const name = NamedRepresentationFactory.defaultNameStrategy(rel); // title will only exist where a resource is passed in AND there is a mapped title. Used for fallback strategy 3. const mappedTitleAttributeName = SparseRepresentationFactory.defaultMappedTitleAttributeName; /** internal helper function to return comparable string from the property of a resource */ function getResourceTitle(obj, prop = mappedTitleAttributeName) { var _a; return (_a = obj === null || obj === void 0 ? void 0 : obj[prop]) === null || _a === void 0 ? void 0 : _a.toLowerCase(); } const resourceTitle = getResourceTitle(where); log.debug('find resource in collection: \'%s\' \'%s\'', name, resourceTitle); // go through the collection and match the URI against either a link relation or attribute return collection .items .find(item => // strategy 1 & 4: Self link of item matches LinkUtil.getUri(item, rel || LinkRelation.Self) === resourceIdentifier || // strategy 2: the attribute on the resource is a uri that matches (name && getResourceTitle(item, name) === resourceIdentifier) || // strategy 3: fallback to mapped title values matching (not uris but titles) (resourceTitle && getResourceTitle(item) === resourceTitle)); } static fields(representation) { return Object.keys(representation) .filter(x => x !== 'links'); } /** * Removes the item from the collection by matching its Self link. If not found, it returns undefined. */ static removeItemFromCollection(collection, item) { const resourceUri = LinkUtil.getUri(item, LinkRelation.Self); const indexOfItemToBeRemoved = collection.items.findIndex(item => LinkUtil.getUri(item, LinkRelation.Self) === resourceUri); if (indexOfItemToBeRemoved >= 0) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [head, _] = collection.items.splice(indexOfItemToBeRemoved, 1); return head; } return undefined; } /** * Removes the item from the collection by matching its Self link. If not found, returns undefined. */ static appendItemToCollection(collection, item) { if (collection.items) { collection.items.splice(collection.items.length, 0, item); } else { log.warn('Collection adding new items array, reactive bindings may fail'); collection.items = [item]; } return collection; } static prependItemToCollection(collection, item) { if (collection.items) { collection.items.unshift(item); } else { log.warn('Collection adding new items array, reactive bindings may fail'); collection.items = [item]; } return collection; } /** * Returns the first item from a collection * * @obsolete this should never be used but rather look for the 'current' on links and return resource */ static current(collection) { if (!collection) { return undefined; } if (instanceOfCollection(collection)) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const [head] = collection.items; return head; } return undefined; } } //# sourceMappingURL=representationUtil.js.map