alcaeus-model
Version:
rdfine models for Alcaeus, Hydra client
124 lines (123 loc) • 4.88 kB
JavaScript
import { rdf } from '@tpluscode/rdf-ns-builders';
function satisfies(criteria, value, actualCheck) {
if (!criteria || !value) {
return true;
}
return actualCheck(criteria, value);
}
function satisfiesMethod(criteria, operation) {
return satisfies(criteria.byMethod, operation.method, (expected, actual) => {
if (typeof expected === 'string') {
return expected.toUpperCase() === actual.toUpperCase();
}
return expected(actual);
});
}
function matchClass(expected, actual) {
if (typeof expected === 'string') {
return actual.id.value === expected;
}
if (typeof expected === 'function') {
return expected(actual);
}
if ('id' in expected) {
return expected.id.equals(actual.id);
}
return actual.id.equals(expected);
}
function satisfiesExpects(criteria, operation) {
if (operation.expects.length === 0) {
return true;
}
return operation.expects.some(expects => satisfies(criteria.expecting, expects, matchClass));
}
function satisfiesReturns(criteria, operation) {
return satisfies(criteria.returning, operation.returns, matchClass);
}
function satisfiesTypeOrId(criteria, operation) {
const supportedOperationId = typeof criteria.bySupportedOperation === 'string'
? operation.env.namedNode(criteria.bySupportedOperation)
: criteria.bySupportedOperation;
return satisfies(supportedOperationId, operation, (expected, actual) => {
if ('termType' in expected) {
return actual.equals(expected) || actual.pointer.has(rdf.type, expected).terms.length > 0;
}
return expected(actual);
});
}
function createMatcher(operation) {
return (criteria) => {
if (!criteria.byMethod) {
criteria.byMethod = method => method.toUpperCase() !== 'GET';
}
return satisfiesReturns(criteria, operation) &&
satisfiesExpects(criteria, operation) &&
satisfiesMethod(criteria, operation) &&
satisfiesTypeOrId(criteria, operation);
};
}
const onlyExplicitNamespaces = ({ namespaces = [] }) => {
const namespaceUris = namespaces.map(ns => typeof ns === 'string' ? ns : ns().value);
return (quad) => namespaceUris.some(ns => quad.predicate.value.startsWith(ns));
};
const excludedProperties = ({ excludedProperties = [] }) => {
const propertiesToExclude = excludedProperties.map(ex => {
if (typeof ex === 'string') {
return ex;
}
if ('id' in ex) {
return ex.id.value;
}
return ex.value;
});
return (quad) => {
return !propertiesToExclude.includes(quad.predicate.value);
};
};
function toResourceNodes(self, mixins) {
return (nodes, quad) => {
if (quad.object.termType === 'NamedNode' || quad.object.termType === 'BlankNode') {
return [...nodes, self._create(self.pointer.node(quad.object), mixins)];
}
return nodes;
};
}
export function OperationFinderMixin(Base) {
return class OperationFinderClass extends Base {
getOperationsDeep(stopConditions = { namespaces: [] }, previousResources = []) {
const childResources = [...this.pointer.dataset.match(this.id, null, null, this._graphId)]
.filter(onlyExplicitNamespaces(stopConditions))
.filter(excludedProperties(stopConditions))
.reduce(toResourceNodes(this, [OperationFinderMixin]), []);
return childResources.reduce((operations, child, index, resources) => {
if (previousResources.find(previous => previous.id.equals(child.id)))
return operations;
const currentlyVisited = [...resources, ...previousResources, this];
const childOps = child.getOperationsDeep(stopConditions, currentlyVisited);
return [
...operations,
...childOps,
];
}, this.operations || []);
}
findOperations(...criteria) {
return this.__filterOperations(this.operations, criteria);
}
findOperationsDeep(stopCondition, ...moreCriteria) {
return this.__filterOperations(this.getOperationsDeep(stopCondition), [stopCondition, ...moreCriteria]);
}
__filterOperations(operations, criteria = []) {
const actualCriteria = [...criteria];
if (actualCriteria.length === 0) {
actualCriteria.push({});
}
return operations.reduce((operations, operation) => {
if (actualCriteria.find(createMatcher(operation))) {
operations.push(operation);
}
return operations;
}, []);
}
};
}
OperationFinderMixin.shouldApply = true;