semantic-network
Version:
A utility library for manipulating a list of links that form a semantic interface to a network of resources.
168 lines • 8.17 kB
JavaScript
import { __awaiter } from "tslib";
import { RepresentationUtil } from './representationUtil';
import { LinkUtil } from 'semantic-link';
import { noopResolver } from '../representation/resourceMergeFactory';
import { SparseRepresentationFactory } from '../representation/sparseRepresentationFactory';
import { LinkRelation } from '../linkRelation';
import anylogger from 'anylogger';
import { LinkRelConvertUtil } from './linkRelConvertUtil';
import { FieldType } from '../types/formTypes';
import { instanceOfCollection } from './instanceOf/instanceOfCollection';
const log = anylogger('FormUtil');
export class FormUtil {
static fieldsToAccept(form, defaultFields = []) {
const fieldsFromForm = (form.items || []).map(x => x.name);
const allFields = defaultFields === null || defaultFields === void 0 ? void 0 : defaultFields.concat(fieldsFromForm);
const distinctFields = new Set(allFields);
return Array.from(distinctFields);
}
/**
* Pick all the fields to resolve in the document at that (a) exist in the form AND (b) exist on the document itself
* or are defaults
* @param document
* @param form
* @param defaultFields
*/
static fieldsToResolve(document, form, defaultFields = []) {
// preparation: get all the fields to return back to the API
const fieldsToReturn = FormUtil.fieldsToAccept(form, defaultFields);
// pick all the fields as specified from the form
const fields = RepresentationUtil.fields(document);
// resolve
return (fieldsToReturn ?
fields.filter(fieldName => fieldsToReturn.includes(fieldName)) :
fields);
}
/**
* Pick all the fields to resolve in the document at that (a) exist in the form AND (b) exist on the document itself
* or are defaults
* @param document
* @param form
* @param defaultFields
* @returns
*
*/
static linksToResolve(document, form, defaultFields = []) {
// preparation: get all the fields to return back to the API
// pick only link rels that exist in the form
// const relsToReturn = fieldsToReturn.map(field => LinkRelConvertUtil.camelToDash(field as string));
// link relations exist as a dashed form and fields/create forms use camel case
// return (relsToReturn ?
// relsToReturn.filter(fieldName => LinkUtil.matches(document as LinkedRepresentation, fieldName)) :
// []) as P[];
return FormUtil.fieldsToAccept(form, defaultFields)
.flatMap(field => LinkRelConvertUtil.camelToDash(field))
.filter(fieldName => LinkUtil.matches(document, fieldName));
}
/**
* Find a {@link FormItem} by matching its {@link FormItem.name} against a field name. A fieldname strategy
* is camel cased
*
* @see LinkRelConvertUtil.dashToCamel
*
* TODO: field could accept RelationshipType {@see LinkRelConvertUtil.relTypeToCamel}
*
* @param form
* @param field
*/
static findByField(form, field) {
var _a;
return (_a = form.items) === null || _a === void 0 ? void 0 : _a.find(item => item.name === field);
}
/**
* A basic dirty check type function comparing an original resource with a new document.
* @param resource original document
* @param document updates to be made
* @param form
* @param defaultFields that require update
* @returns fields to merge that actually requiring updating based on being a different value
*
* // TODO; return Omit<P, "links">
*/
static fieldsRequiringUpdate(resource, document, form, defaultFields = []) {
const fieldsToCheck = FormUtil.fieldsToAccept(form, defaultFields);
// only return fields that are different
return fieldsToCheck
.filter(field => {
// omit any fields that match
// WARNING: This might have problems if the field is a 'multiple' <<<<<<<<<<<<<<<< ---- please review
return !(RepresentationUtil.getProperty(resource, field) === RepresentationUtil.getProperty(document, field));
});
}
/**
* Returns a new object with only fields explicitly set in the form or default values
* @param document
* @param form
* @param options
*/
static fieldsToReturnFromForm(document, form, options) {
const { defaultFields = [] } = Object.assign({}, options);
const fieldsToResolve = FormUtil.fieldsToResolve(document, form, defaultFields);
const doc = {};
for (const field of fieldsToResolve) {
// really not sure how to avoid indexer errors
doc[field] = RepresentationUtil.getProperty(document, field);
}
return doc;
}
/**
* A {@link FormItem} can have a lazy loaded {@link FieldType.Collection} on a {@link FieldType.Select} where the
* {@link FormItem.id} is set to the uri of the collection that is resolved via an optional {@link MergeOptions.resourceResolver}.
* This collection may or may not be resolved at the same time via an optional {@link MergeOptions.resolver}.
*
* Note: there is a current implicit polymorphism to be resolved. The {@link FormItem.items} can be both a {@link FormItem[]}
* and a {@link CollectionRepresentation} where the code looks through to the Collection items.
*
* @param item form item that may optionally have a lazy loaded items collection
* @param options contains the optional resolvers
*/
static resolveItemsFromCollection(item, options) {
return __awaiter(this, void 0, void 0, function* () {
if (item.type !== FieldType.Select) {
log.warn('Only selects form type should be called but have called %s', item.type);
}
if (item.id) {
//
const { resourceResolver = undefined, resolver = noopResolver } = Object.assign({}, options);
if (resourceResolver) {
const uri = resolver.resolve(item.id);
const representation = SparseRepresentationFactory.make(Object.assign(Object.assign({}, options), { uri }));
log.debug('matching items collection with resource resolver \'%s\' on \'%s\' --> \'%s\'', item.name, item.id, uri);
const resource = yield resourceResolver(item.name)(representation, options);
if (instanceOfCollection(resource)) {
// put the collection onto the items.
// TODO: merge onto existing items
// TODO: workout how to deal with Collection versus FormItem[]
if (item.items) {
log.warn('Potential conflict between existing form items and merged collection');
}
item['items'] = resource;
}
else {
if (resource) {
log.error('Only a collection may be lazy loaded onto form items: %s', LinkUtil.getUri(resource, LinkRelation.Self));
} // do nothing to the items
}
} // else no resolver
} // else no 'id' is just fine and do nothing
// on a FormItem, items is optional but it is easier if instead of null, items is an empty list where the form type
// is select
if (!item.items) {
item.items = [];
}
});
}
/**
* Take a form representation and return back a document based on the keys in the form representation.
*
* TODO: values are currently null but could include any defaults from the form
*/
static toEmptyDocument(form) {
const formItems = form.items || [];
const documentation = formItems.map(key => ({ [key.name]: null }));
// ensure that it is cloned
//?? check, shouldn't need this
return Object.assign({}, ...documentation);
}
}
//# sourceMappingURL=formUtil.js.map