@api-components/amf-helper-mixin
Version:
A mixin with common functions user by most AMF components to compute AMF values
1,348 lines (1,306 loc) • 82.6 kB
JavaScript
/* eslint-disable class-methods-use-this */
import { AmfHelperMixin, expandKey, findAmfType, getArrayItems } from "./AmfHelperMixin.js";
/** @typedef {import('./types').ApiParametrizedSecurityScheme} ApiParametrizedSecurityScheme */
/** @typedef {import('./types').ApiRequest} ApiRequest */
/** @typedef {import('./types').ApiSecurityScheme} ApiSecurityScheme */
/** @typedef {import('./types').ApiSecurityRequirement} ApiSecurityRequirement */
/** @typedef {import('./types').ApiTemplatedLink} ApiTemplatedLink */
/** @typedef {import('./types').ApiResponse} ApiResponse */
/** @typedef {import('./types').ApiPayload} ApiPayload */
/** @typedef {import('./types').ApiExample} ApiExample */
/** @typedef {import('./types').ApiParameter} ApiParameter */
/** @typedef {import('./types').ApiOperation} ApiOperation */
/** @typedef {import('./types').ApiEndPoint} ApiEndPoint */
/** @typedef {import('./types').ApiServer} ApiServer */
/** @typedef {import('./types').ApiDocumentation} ApiDocumentation */
/** @typedef {import('./types').ApiShape} ApiShape */
/** @typedef {import('./types').ApiPropertyShape} ApiPropertyShape */
/** @typedef {import('./types').ApiAnyShape} ApiAnyShape */
/** @typedef {import('./types').ApiNodeShape} ApiNodeShape */
/** @typedef {import('./types').ApiScalarShape} ApiScalarShape */
/** @typedef {import('./types').ApiUnionShape} ApiUnionShape */
/** @typedef {import('./types').ApiFileShape} ApiFileShape */
/** @typedef {import('./types').ApiDataArrangeShape} ApiDataArrangeShape */
/** @typedef {import('./types').ApiXMLSerializer} ApiXMLSerializer */
/** @typedef {import('./types').ApiDataNode} ApiDataNode */
/** @typedef {import('./types').ApiScalarNode} ApiScalarNode */
/** @typedef {import('./types').ApiObjectNode} ApiObjectNode */
/** @typedef {import('./types').ApiArrayNode} ApiArrayNode */
/** @typedef {import('./types').ApiSchemaShape} ApiSchemaShape */
/** @typedef {import('./types').ApiArrayShape} ApiArrayShape */
/** @typedef {import('./types').ApiTupleShape} ApiTupleShape */
/** @typedef {import('./types').ApiShapeUnion} ApiShapeUnion */
/** @typedef {import('./types').ApiSecuritySettings} ApiSecuritySettings */
/** @typedef {import('./types').ApiSecurityOAuth1Settings} ApiSecurityOAuth1Settings */
/** @typedef {import('./types').ApiSecurityOAuth2Settings} ApiSecurityOAuth2Settings */
/** @typedef {import('./types').ApiSecurityApiKeySettings} ApiSecurityApiKeySettings */
/** @typedef {import('./types').ApiSecurityHttpSettings} ApiSecurityHttpSettings */
/** @typedef {import('./types').ApiSecurityOpenIdConnectSettings} ApiSecurityOpenIdConnectSettings */
/** @typedef {import('./types').ApiSecurityOAuth2Flow} ApiSecurityOAuth2Flow */
/** @typedef {import('./types').ApiSecuritySettingsUnion} ApiSecuritySettingsUnion */
/** @typedef {import('./types').ApiSecurityScope} ApiSecurityScope */
/** @typedef {import('./types').ApiIriTemplateMapping} ApiIriTemplateMapping */
/** @typedef {import('./types').ApiCallback} ApiCallback */
/** @typedef {import('./types').ApiDomainProperty} ApiDomainProperty */
/** @typedef {import('./types').ApiCustomDomainProperty} ApiCustomDomainProperty */
/** @typedef {import('./types').ApiRecursiveShape} ApiRecursiveShape */
/** @typedef {import('./types').ApiTag} ApiTag */
/** @typedef {import('./types').ApiDataNodeUnion} ApiDataNodeUnion */
/** @typedef {import('./types').ApiDocumentSourceMaps} ApiDocumentSourceMaps */
/** @typedef {import('./types').ApiSynthesizedField} ApiSynthesizedField */
/** @typedef {import('./types').ApiParametrizedDeclaration} ApiParametrizedDeclaration */
/** @typedef {import('./types').ApiParametrizedTrait} ApiParametrizedTrait */
/** @typedef {import('./types').ApiParametrizedResourceType} ApiParametrizedResourceType */
/** @typedef {import('./types').ApiVariableValue} ApiVariableValue */
/** @typedef {import('./types').ApiAbstractDeclaration} ApiAbstractDeclaration */
/** @typedef {import('./types').ShapeProcessingOptions} ShapeProcessingOptions */
/** @typedef {import('./types').ApiSummary} ApiSummary */
/** @typedef {import('./types').ApiOrganization} ApiOrganization */
/** @typedef {import('./types').ApiLicense} ApiLicense */
/** @typedef {import('./types').ApiBase} ApiBase */
/** @typedef {import('./types').ApiWeb} ApiWeb */
/** @typedef {import('./types').ApiAsync} ApiAsync */
/** @typedef {import('./amf').Api} Api */
/** @typedef {import('./amf').WebApi} WebApi */
/** @typedef {import('./amf').AsyncApi} AsyncApi */
/** @typedef {import('./amf').Server} Server */
/** @typedef {import('./amf').Parameter} Parameter */
/** @typedef {import('./amf').Shape} Shape */
/** @typedef {import('./amf').ScalarShape} ScalarShape */
/** @typedef {import('./amf').NodeShape} NodeShape */
/** @typedef {import('./amf').UnionShape} UnionShape */
/** @typedef {import('./amf').FileShape} FileShape */
/** @typedef {import('./amf').SchemaShape} SchemaShape */
/** @typedef {import('./amf').ArrayShape} ArrayShape */
/** @typedef {import('./amf').TupleShape} TupleShape */
/** @typedef {import('./amf').AnyShape} AnyShape */
/** @typedef {import('./amf').DomainElement} DomainElement */
/** @typedef {import('./amf').PropertyShape} PropertyShape */
/** @typedef {import('./amf').DataArrangeShape} DataArrangeShape */
/** @typedef {import('./amf').CreativeWork} CreativeWork */
/** @typedef {import('./amf').Example} Example */
/** @typedef {import('./amf').XMLSerializer} XMLSerializer */
/** @typedef {import('./amf').DataNode} DataNode */
/** @typedef {import('./amf').ScalarNode} ScalarNode */
/** @typedef {import('./amf').ArrayNode} ArrayNode */
/** @typedef {import('./amf').ObjectNode} ObjectNode */
/** @typedef {import('./amf').RecursiveShape} RecursiveShape */
/** @typedef {import('./amf').EndPoint} EndPoint */
/** @typedef {import('./amf').Operation} Operation */
/** @typedef {import('./amf').Callback} Callback */
/** @typedef {import('./amf').Request} Request */
/** @typedef {import('./amf').Response} Response */
/** @typedef {import('./amf').Payload} Payload */
/** @typedef {import('./amf').TemplatedLink} TemplatedLink */
/** @typedef {import('./amf').IriTemplateMapping} IriTemplateMapping */
/** @typedef {import('./amf').ParametrizedSecurityScheme} ParametrizedSecurityScheme */
/** @typedef {import('./amf').SecurityScheme} SecurityScheme */
/** @typedef {import('./amf').SecurityRequirement} SecurityRequirement */
/** @typedef {import('./amf').Settings} Settings */
/** @typedef {import('./amf').OAuth1Settings} OAuth1Settings */
/** @typedef {import('./amf').OAuth2Settings} OAuth2Settings */
/** @typedef {import('./amf').OAuth2Flow} OAuth2Flow */
/** @typedef {import('./amf').Scope} Scope */
/** @typedef {import('./amf').ApiKeySettings} ApiKeySettings */
/** @typedef {import('./amf').HttpSettings} HttpSettings */
/** @typedef {import('./amf').OpenIdConnectSettings} OpenIdConnectSettings */
/** @typedef {import('./amf').Tag} Tag */
/** @typedef {import('./amf').DocumentSourceMaps} DocumentSourceMaps */
/** @typedef {import('./amf').SynthesizedField} SynthesizedField */
/** @typedef {import('./amf').ParametrizedDeclaration} ParametrizedDeclaration */
/** @typedef {import('./amf').ParametrizedTrait} ParametrizedTrait */
/** @typedef {import('./amf').ParametrizedResourceType} ParametrizedResourceType */
/** @typedef {import('./amf').VariableValue} VariableValue */
/** @typedef {import('./amf').AbstractDeclaration} AbstractDeclaration */
/** @typedef {import('./amf').Organization} Organization */
/** @typedef {import('./amf').License} License */
/**
* A class that takes AMF's ld+json model and outputs JavaScript interface of it.
*/
export class AmfSerializer extends AmfHelperMixin(Object) {
/**
* @param {DomainElement=} graph Optional AMF generated graph model.
*/
constructor(graph) {
super();
if (graph) {
this.amf = graph;
}
}
/**
* @param {string[]} types The list of graph object types. When not defined it returns an empty array.
* @returns {string[]} The expanded types.
*/
readTypes(types) {
let target = types;
if (typeof target === 'string') {
target = [target];
}
if (!Array.isArray(target)) {
return [];
}
return target.map(this[expandKey].bind(this));
}
/**
* @param {Api} object The API to serialize.
* @returns {ApiSummary} API summary, without complex objects.
*/
apiSummary(object) {
const result = /** @type ApiSummary */ ({
id: object['@id'],
types: this.readTypes(object['@type']),
customDomainProperties: this.customDomainProperties(object),
sourceMaps: this.sourceMap(object),
schemes: [],
accepts: [],
contentType: [],
documentations: [],
tags: [],
});
const { ns } = this;
const name = this._getValue(object, ns.aml.vocabularies.core.name);
if (name && typeof name === 'string') {
result.name = name;
}
const description = this._getValue(object, ns.aml.vocabularies.core.description);
if (description && typeof description === 'string') {
result.description = description;
}
const version = this._getValue(object, ns.aml.vocabularies.core.version);
if (version && typeof version === 'string') {
result.version = version;
}
const termsOfService = this._getValue(object, ns.aml.vocabularies.core.termsOfService);
if (termsOfService && typeof termsOfService === 'string') {
result.termsOfService = termsOfService;
}
const accepts = object[this._getAmfKey(ns.aml.vocabularies.apiContract.accepts)];
if (Array.isArray(accepts) && accepts.length) {
result.accepts = /** @type string[] */ (this._getValueArray(object, ns.aml.vocabularies.apiContract.accepts));
}
const contentType = object[this._getAmfKey(ns.aml.vocabularies.apiContract.contentType)];
if (Array.isArray(contentType) && contentType.length) {
result.contentType = /** @type string[] */ (this._getValueArray(object, ns.aml.vocabularies.apiContract.contentType));
}
const schemes = object[this._getAmfKey(ns.aml.vocabularies.apiContract.scheme)];
if (Array.isArray(schemes) && schemes.length) {
result.schemes = /** @type string[] */ (this._getValueArray(object, ns.aml.vocabularies.apiContract.scheme));
}
let provider = object[this._getAmfKey(ns.aml.vocabularies.core.provider)];
if (Array.isArray(provider)) {
[provider] = provider;
}
if (provider) {
result.provider = this.provider(provider);
}
let license = object[this._getAmfKey(ns.aml.vocabularies.core.license)];
if (Array.isArray(license)) {
[license] = license;
}
if (license) {
result.license = this.license(license);
}
const tags = object[this._getAmfKey(ns.aml.vocabularies.apiContract.tag)];
if (Array.isArray(tags) && tags.length) {
result.tags = tags.map(t => this.tag(t));
}
const docs = object[this._getAmfKey(ns.aml.vocabularies.core.documentation)];
if (Array.isArray(docs) && docs.length) {
result.documentations = docs.map(d => this.documentation(d));
}
return result;
}
/**
* @param {Api} object
* @returns {ApiBase}
*/
api(object) {
const result = /** @type ApiBase */ (this.apiSummary(object));
result.endPoints = [];
result.servers = [];
result.security = [];
const { ns } = this;
const endPoints = object[this._getAmfKey(ns.aml.vocabularies.apiContract.endpoint)];
if (Array.isArray(endPoints) && endPoints.length) {
result.endPoints = endPoints.map(e => this.endPoint(e));
}
const servers = object[this._getAmfKey(ns.aml.vocabularies.apiContract.server)];
if (Array.isArray(servers) && servers.length) {
result.servers = servers.map(s => this.server(s));
}
const security = object[this._getAmfKey(ns.aml.vocabularies.security.security)];
if (Array.isArray(security) && security.length) {
result.security = security.map(s => this.securityRequirement(s));
}
return result;
}
/**
* @param {WebApi} object
* @returns {ApiWeb}
*/
webApi(object) {
return this.api(object);
}
/**
* @param {AsyncApi} object
* @returns {ApiAsync}
*/
asyncApi(object) {
return this.api(object);
}
/**
* @param {Organization} object
* @returns {ApiOrganization}
*/
provider(object) {
const result = /** @type ApiOrganization */ ({
id: object['@id'],
types: this.readTypes(object['@type']),
customDomainProperties: this.customDomainProperties(object),
sourceMaps: this.sourceMap(object),
});
const { ns } = this;
const name = this._getValue(object, ns.aml.vocabularies.core.name);
if (name && typeof name === 'string') {
result.name = name;
}
const url = this._getLinkValue(object, ns.aml.vocabularies.core.url);
if (url && typeof url === 'string') {
result.url = url;
}
const email = this._getValue(object, ns.aml.vocabularies.core.email);
if (email && typeof email === 'string') {
result.email = email;
}
return result;
}
/**
* @param {License} object
* @returns {ApiLicense}
*/
license(object) {
const result = /** @type ApiLicense */ ({
id: object['@id'],
types: this.readTypes(object['@type']),
customDomainProperties: this.customDomainProperties(object),
sourceMaps: this.sourceMap(object),
});
const { ns } = this;
const name = this._getValue(object, ns.aml.vocabularies.core.name);
if (name && typeof name === 'string') {
result.name = name;
}
const url = this._getLinkValue(object, ns.aml.vocabularies.core.url);
if (url && typeof url === 'string') {
result.url = url;
}
return result;
}
/**
* @param {Server} object The AMF Server to serialize.
* @returns {ApiServer} Serialized Server
*/
server(object) {
const { ns } = this;
const url = this._getValue(object, ns.aml.vocabularies.core.urlTemplate) || '';
const result = /** @type ApiServer */ ({
id: object['@id'],
types: this.readTypes(object['@type']),
url,
variables: [],
customDomainProperties: this.customDomainProperties(object),
sourceMaps: this.sourceMap(object),
});
const description = this._getValue(object, ns.aml.vocabularies.core.description);
if (description && typeof description === 'string') {
result.description = description;
}
const variables = /** @type Parameter[] */ (object[this._getAmfKey(ns.aml.vocabularies.apiContract.variable)]);
if (Array.isArray(variables) && variables.length) {
result.variables = variables.map((p) => this.parameter(p));
}
const protocol = /** @type string */ (this._getValue(object, ns.aml.vocabularies.apiContract.protocol));
const protocolVersion = /** @type string */ (this._getValue(object, ns.aml.vocabularies.apiContract.protocolVersion));
if (protocol) {
result.protocol = protocol;
}
if (protocolVersion) {
result.protocolVersion = protocolVersion;
}
const security = /** @type SecurityRequirement */ (object[this._getAmfKey(ns.aml.vocabularies.security.security)]);
if (Array.isArray(security) && security.length) {
result.security = security.map((p) => this.securityRequirement(p));
}
return result;
}
/**
* @param {Parameter} object The Parameter to serialize.
* @returns {ApiParameter} Serialized Parameter
*/
parameter(object) {
const result = /** @type ApiParameter */ ({
id: object['@id'],
types: this.readTypes(object['@type']),
payloads: [],
examples: [],
customDomainProperties: this.customDomainProperties(object),
sourceMaps: this.sourceMap(object),
});
const { ns } = this;
const name = this._getValue(object, ns.aml.vocabularies.core.name);
if (name && typeof name === 'string') {
result.name = name;
}
const paramName = this._getValue(object, ns.aml.vocabularies.apiContract.paramName);
if (paramName && typeof paramName === 'string') {
result.paramName = paramName;
}
const description = this._getValue(object, ns.aml.vocabularies.core.description);
if (description && typeof description === 'string') {
result.description = description;
}
const required = this._getValue(object, ns.aml.vocabularies.apiContract.required);
if (typeof required === 'boolean') {
result.required = required;
}
const allowEmptyValue = this._getValue(object, ns.aml.vocabularies.apiContract.allowEmptyValue);
if (typeof allowEmptyValue === 'boolean') {
result.allowEmptyValue = allowEmptyValue;
}
const deprecated = this._getValue(object, ns.aml.vocabularies.document.deprecated);
if (typeof deprecated === 'boolean') {
result.deprecated = deprecated;
}
const explode = this._getValue(object, ns.aml.vocabularies.apiContract.explode);
if (typeof explode === 'boolean') {
result.explode = explode;
}
const allowReserved = this._getValue(object, ns.aml.vocabularies.apiContract.allowReserved);
if (typeof allowReserved === 'boolean') {
result.allowReserved = allowReserved;
}
const style = this._getValue(object, ns.aml.vocabularies.apiContract.style);
if (style && typeof style === 'string') {
result.style = style;
}
const binding = this._getValue(object, ns.aml.vocabularies.apiContract.binding);
if (binding && typeof binding === 'string') {
result.binding = binding;
}
const schemas = object[this._getAmfKey(ns.aml.vocabularies.shapes.schema)];
if (Array.isArray(schemas) && schemas.length) {
const [schema] = schemas;
result.schema = this.unknownShape(schema, {
trackedId: object['@id'],
});
}
const payloads = object[this._getAmfKey(ns.aml.vocabularies.apiContract.payload)];
if (Array.isArray(payloads) && payloads.length) {
result.payloads = payloads.map(p => this.payload(p));
}
const examples = object[this._getAmfKey(ns.aml.vocabularies.apiContract.examples)];
if (Array.isArray(examples) && examples.length) {
result.examples = examples.map(e => this.example(e));
}
return result;
}
/**
* @param {Shape} object
* @param {ShapeProcessingOptions=} options
* @returns {ApiShapeUnion}
*/
unknownShape(object, options) {
const types = this.readTypes(object['@type']);
const { ns } = this;
if (types.includes(ns.aml.vocabularies.shapes.ScalarShape)) {
return this.scalarShape(/** @type ScalarShape */ (object), options);
}
if (types.includes(ns.w3.shacl.NodeShape)) {
return this.nodeShape(/** @type NodeShape */ (object), options);
}
if (types.includes(ns.aml.vocabularies.shapes.UnionShape)) {
return this.unionShape(/** @type UnionShape */ (object), options);
}
if (types.includes(ns.aml.vocabularies.shapes.FileShape)) {
return this.fileShape(/** @type FileShape */ (object), options);
}
if (types.includes(ns.aml.vocabularies.shapes.SchemaShape)) {
return this.schemaShape(/** @type SchemaShape */ (object), options);
}
// this must be before the ArrayShape
if (types.includes(ns.aml.vocabularies.shapes.TupleShape)) {
return this.tupleShape(/** @type TupleShape */ (object), options);
}
if (types.includes(ns.aml.vocabularies.shapes.ArrayShape) || types.includes(ns.aml.vocabularies.shapes.MatrixShape)) {
return this.arrayShape(/** @type ArrayShape */ (object), options);
}
if (types.includes(ns.aml.vocabularies.shapes.RecursiveShape)) {
return this.recursiveShape(/** @type RecursiveShape */ (object));
}
// recursiveShape
return this.anyShape(/** @type AnyShape */ (object), options);
}
/**
* @param {DomainElement} object
* @returns {boolean}
*/
isLink(object) {
return !!this._getLinkValue(object, this.ns.aml.vocabularies.document.linkTarget);
}
/**
* @param {DomainElement} object
* @returns {DomainElement|undefined}
*/
getLinkTarget(object) {
const id = this._getLinkValue(object, this.ns.aml.vocabularies.document.linkTarget);
return this[findAmfType](id);
}
/**
* @param {Shape} object
* @returns {ApiShape}
*/
shape(object) {
this._resolve(object);
/** @type string */
let linkLabel;
let target = object;
if (this.isLink(target)) {
linkLabel = /** @type string */ (this._getValue(target, this.ns.aml.vocabularies.document.linkLabel));
const id = this._getLinkValue(target, this.ns.aml.vocabularies.document.linkTarget);
const value = /** @type Shape */ (this[findAmfType](id));
if (value) {
target = value;
}
}
const result = /** @type ApiShape */ ({
id: target['@id'],
types: this.readTypes(object['@type']),
values: [],
inherits: [],
or: [],
and: [],
xone: [],
customDomainProperties: this.customDomainProperties(object),
sourceMaps: this.sourceMap(object),
});
if (linkLabel) {
result.linkLabel = linkLabel;
}
const { ns } = this;
const name = this._getValue(target, ns.w3.shacl.name);
if (name && typeof name === 'string') {
result.name = name;
}
const displayName = this._getValue(target, ns.aml.vocabularies.core.displayName);
if (displayName && typeof displayName === 'string') {
result.displayName = displayName;
} else {
const coreName = this._getValue(target, ns.aml.vocabularies.core.name);
if (coreName && typeof coreName === 'string') {
result.displayName = coreName;
}
}
const description = this._getValue(target, ns.aml.vocabularies.core.description);
if (description && typeof description === 'string') {
result.description = description;
}
const defaultValueStr = this._getValue(target, ns.w3.shacl.defaultValueStr);
if (defaultValueStr && typeof defaultValueStr === 'string') {
result.defaultValueStr = defaultValueStr;
}
const deprecated = this._getValue(target, ns.aml.vocabularies.shapes.deprecated);
if (typeof deprecated === 'boolean') {
result.deprecated = deprecated;
}
const readOnly = this._getValue(target, ns.aml.vocabularies.shapes.readOnly);
if (typeof readOnly === 'boolean') {
result.readOnly = readOnly;
}
const writeOnly = this._getValue(target, ns.aml.vocabularies.shapes.writeOnly);
if (typeof writeOnly === 'boolean') {
result.writeOnly = writeOnly;
}
const defValue = target[this._getAmfKey(ns.w3.shacl.defaultValue)];
if (Array.isArray(defValue) && defValue.length) {
result.defaultValue = this.unknownDataNode(defValue[0]);
}
// @TODO:
// if (Array.isArray(inherits) && inherits.length) {
// result.inherits = inherits.map((item) => this.unknownShape(item));
// }
const orKey = this._getAmfKey(ns.w3.shacl.or);
const orGroup = /** @type Shape[] */ (target[orKey]);
if (Array.isArray(orGroup) && orGroup.length) {
result.or = orGroup.map((item) => this.unknownShape(item));
}
const andKey = this._getAmfKey(ns.w3.shacl.and);
const andGroup = /** @type Shape[] */ (target[andKey]);
if (Array.isArray(andGroup) && andGroup.length) {
result.and = andGroup.map((item) => this.unknownShape(item));
}
const xoneKey = this._getAmfKey(ns.w3.shacl.xone);
const xone = /** @type Shape[] */ (target[xoneKey]);
if (Array.isArray(xone) && xone.length) {
result.xone = xone.map((item) => this.unknownShape(item));
}
const valuesList = target[this._getAmfKey(ns.w3.shacl.in)];
if (Array.isArray(valuesList) && valuesList.length) {
const [values] = valuesList;
const prefix = this.ns.w3.rdfSchema.toString();
const prefixCompact = this._getAmfKey(prefix);
Object.keys(values).forEach((key) => {
if (key.startsWith(prefix) || key.startsWith(prefixCompact)) {
let value = values[key];
if (Array.isArray(value)) {
[value] = value;
}
const processed = this.unknownDataNode(value);
result.values.push(processed);
}
});
}
const notKey = this._getAmfKey(ns.w3.shacl.not);
let not = /** @type Shape */ (target[notKey]);
if (not) {
if (Array.isArray(not)) {
[not] = not;
}
result.not = this.unknownShape(not);
}
return result;
}
/**
* @param {AnyShape} object
* @param {ShapeProcessingOptions=} options
* @returns {ApiAnyShape}
*/
anyShape(object, options={}) {
let target = object;
const result = /** @type ApiAnyShape */ (this.shape(target));
if (this.isLink(target)) {
const value = /** @type Shape */ (this.getLinkTarget(target));
if (value) {
target = value;
}
}
result.examples = [];
const { ns } = this;
const examples = target[this._getAmfKey(ns.aml.vocabularies.apiContract.examples)];
if (Array.isArray(examples) && examples.length) {
if (options.trackedId) {
const filtered = this.filterTrackedExamples(examples, options.trackedId);
result.examples = filtered.map((item) => this.example(item));
} else {
const filtered = this.filterNonTrackedExamples(examples);
result.examples = filtered.map((item) => this.example(item));
}
}
const docs = target[this._getAmfKey(ns.aml.vocabularies.core.documentation)];
if (Array.isArray(docs) && docs.length) {
const [documentation] = docs;
result.documentation = this.documentation(documentation);
}
const xml = target[this._getAmfKey(ns.aml.vocabularies.shapes.xmlSerialization)];
if (Array.isArray(xml) && xml.length) {
result.xmlSerialization = this.xmlSerializer(xml[0]);
}
return result;
}
/**
* Filters examples that should be rendered for a payload identified by `trackedId`.
*
* This function is copied from old `api-example-generator/ExampleGenerator`.
*
* @param {Example[]} examples
* @param {string} trackedId
* @returns {Example[]}
*/
filterTrackedExamples(examples, trackedId) {
const { docSourceMaps } = this.ns.raml.vocabularies;
const sourceKey = this._getAmfKey(docSourceMaps.sources);
const trackedKey = this._getAmfKey(docSourceMaps.trackedElement);
const longId = trackedId.indexOf('amf') === -1 ? `amf://id${trackedId}` : trackedId;
return examples.filter((item) => {
let example = item;
if (Array.isArray(example)) {
[example] = example;
}
let sm = example[sourceKey];
if (!sm) {
return true
}
if (Array.isArray(sm)) {
[sm] = sm;
}
let tracked = sm[trackedKey];
if (!tracked) {
return true;
}
if (Array.isArray(tracked)) {
[tracked] = tracked;
}
const { value } = this.synthesizedField(tracked);
if (!value) {
return true;
}
const ids = value.split(',');
if (ids.indexOf(longId) !== -1 || ids.indexOf(trackedId) !== -1) {
return true;
}
return false;
});
}
/**
* Kind of the opposite of the `filterTrackedExamples`. It gathers examples that only have been
* defined for the parent Shape (ed in the type declaration). It filters out all examples
* defined in a payload.
*
* @param {Example[]} examples
* @returns {Example[]}
*/
filterNonTrackedExamples(examples) {
const { docSourceMaps } = this.ns.raml.vocabularies;
const sourceKey = this._getAmfKey(docSourceMaps.sources);
const trackedKey = this._getAmfKey(docSourceMaps.trackedElement);
return examples.filter((item) => {
let example = item;
if (Array.isArray(example)) {
[example] = example;
}
let sm = example[sourceKey];
if (!sm) {
return true
}
if (Array.isArray(sm)) {
[sm] = sm;
}
let tracked = sm[trackedKey];
if (!tracked) {
return true;
}
if (Array.isArray(tracked)) {
[tracked] = tracked;
}
const { value } = this.synthesizedField(tracked);
if (!value) {
return true;
}
return false;
});
}
/**
* @param {ScalarShape} object
* @param {ShapeProcessingOptions=} options
* @returns {ApiScalarShape}
*/
scalarShape(object, options={}) {
let target = object;
const result = /** @type ApiScalarShape */ (this.anyShape(target, options));
if (this.isLink(target)) {
const value = /** @type ScalarShape */ (this.getLinkTarget(target));
if (value) {
target = value;
}
}
const { ns } = this;
const pattern = this._getValue(target, ns.w3.shacl.pattern);
if (pattern && typeof pattern === 'string') {
result.pattern = pattern;
}
const dataType = this._getLinkValue(target, ns.w3.shacl.datatype);
if (dataType && typeof dataType === 'string') {
result.dataType = dataType;
}
const format = this._getValue(target, ns.aml.vocabularies.shapes.format);
if (format && typeof format === 'string') {
result.format = format;
}
const multipleOf = this._getValue(target, ns.aml.vocabularies.shapes.multipleOf);
if (typeof multipleOf === 'number') {
result.multipleOf = multipleOf;
}
const minInclusive = this._getValue(target, ns.w3.shacl.minInclusive);
if (typeof minInclusive === 'number') {
result.minimum = minInclusive;
result.exclusiveMinimum = false;
}
const maxInclusive = this._getValue(target, ns.w3.shacl.maxInclusive);
if (typeof maxInclusive === 'number') {
result.maximum = maxInclusive;
result.exclusiveMaximum = false;
}
const minLength = this._getValue(target, ns.w3.shacl.minLength);
if (typeof minLength === 'number') {
result.minLength = minLength;
}
const maxLength = this._getValue(target, ns.w3.shacl.maxLength);
if (typeof maxLength === 'number') {
result.maxLength = maxLength;
}
return result;
}
/**
* @param {NodeShape} object The NodeShape to serialize
* @param {ShapeProcessingOptions=} options
* @returns {ApiNodeShape}
*/
nodeShape(object, options={}) {
let target = object;
const result = /** @type ApiNodeShape */ (this.anyShape(target, options));
if (this.isLink(target)) {
const value = /** @type NodeShape */ (this.getLinkTarget(target));
if (value) {
target = value;
}
}
const { ns } = this;
const discriminator = this._getValue(target, ns.aml.vocabularies.shapes.discriminator);
if (discriminator && typeof discriminator === 'string') {
result.discriminator = discriminator;
}
const discriminatorValue = this._getValue(target, ns.aml.vocabularies.shapes.discriminatorValue);
if (discriminatorValue && typeof discriminatorValue === 'string') {
result.discriminatorValue = discriminatorValue;
}
const closed = this._getValue(target, ns.w3.shacl.closed);
if (typeof closed === 'boolean') {
result.closed = closed;
}
result.customShapeProperties = [];
result.customShapePropertyDefinitions = [];
result.dependencies = [];
// todo: not sure what the keys are.
// if (!minProperties.isNull) {
// result.minProperties = minProperties.value();
// }
// if (!maxProperties.isNull) {
// result.maxProperties = maxProperties.value();
// }
// if (Array.isArray(customShapeProperties) && customShapeProperties.length) {
// result.customShapeProperties = customShapeProperties.map((item) => item.id);
// } else {
// result.customShapeProperties = [];
// }
// if (Array.isArray(customShapePropertyDefinitions) && customShapePropertyDefinitions.length) {
// result.customShapePropertyDefinitions = customShapePropertyDefinitions.map((item) => item.id);
// } else {
// result.customShapePropertyDefinitions = [];
// }
const properties = /** @type PropertyShape[] */ (target[this._getAmfKey(ns.w3.shacl.property)]);
if (Array.isArray(properties) && properties.length) {
result.properties = properties.map((item) => this.propertyShape(item));
} else {
result.properties = [];
}
// if (Array.isArray(dependencies) && dependencies.length) {
// result.dependencies = dependencies.map((item) => item.id);
// } else {
// result.dependencies = [];
// }
return result;
}
/**
* @param {PropertyShape} object
* @returns {ApiPropertyShape}
*/
propertyShape(object) {
let target = object;
const result = /** @type ApiPropertyShape */ (this.shape(target));
if (this.isLink(target)) {
const value = /** @type PropertyShape */ (this.getLinkTarget(target));
if (value) {
target = value;
}
}
const { ns } = this;
const path = this._getLinkValue(target, ns.w3.shacl.path);
if (path && typeof path === 'string') {
result.path = path;
}
const minCount = this._getValue(target, ns.w3.shacl.minCount);
if (typeof minCount === 'number') {
result.minCount = minCount;
}
const maxCount = this._getValue(target, ns.w3.shacl.maxCount);
if (typeof maxCount === 'number') {
result.maxCount = maxCount;
}
// if (!patternName.isNullOrEmpty) {
// result.patternName = patternName.value();
// }
const ranges = /** @type Shape[] */ (target[this._getAmfKey(ns.aml.vocabularies.shapes.range)]);
if (Array.isArray(ranges) && ranges.length) {
const [range] = ranges;
result.range = this.unknownShape(range);
}
return result;
}
/**
* @param {UnionShape} object
* @param {ShapeProcessingOptions=} options
* @returns {ApiUnionShape}
*/
unionShape(object, options={}) {
const anyOf = /** @type Shape[] */ (object[this._getAmfKey(this.ns.aml.vocabularies.shapes.anyOf)]);
const result = /** @type ApiUnionShape */ (this.anyShape(object, options));
if (Array.isArray(anyOf) && anyOf.length) {
const opt = { ...options, trackedId: undefined };
result.anyOf = anyOf.map((shape) => this.unknownShape(shape, opt));
} else {
result.anyOf = [];
}
return result;
}
/**
* @param {FileShape} object
* @param {ShapeProcessingOptions=} options
* @returns {ApiFileShape}
*/
fileShape(object, options={}) {
let target = object;
const result = /** @type ApiFileShape */ (this.anyShape(target, options));
if (this.isLink(target)) {
const value = /** @type FileShape */ (this.getLinkTarget(target));
if (value) {
target = value;
}
}
const { ns } = this;
const pattern = this._getValue(target, ns.w3.shacl.pattern);
if (pattern && typeof pattern === 'string') {
result.pattern = pattern;
}
const fileTypes = /** @type string[] */ (this._getValueArray(target, ns.aml.vocabularies.shapes.fileType));
if (Array.isArray(fileTypes) && fileTypes.length) {
result.fileTypes = fileTypes;
}
const minLength = this._getValue(target, ns.w3.shacl.minLength);
if (typeof minLength === 'number') {
result.minLength = minLength;
}
const maxLength = this._getValue(target, ns.w3.shacl.maxLength);
if (typeof maxLength === 'number') {
result.maxLength = maxLength;
}
const minInclusive = this._getValue(target, ns.w3.shacl.minInclusive);
if (typeof minInclusive === 'number') {
result.minimum = minInclusive;
result.exclusiveMinimum = false;
}
const maxInclusive = this._getValue(target, ns.w3.shacl.maxInclusive);
if (typeof maxInclusive === 'number') {
result.maximum = maxInclusive;
result.exclusiveMaximum = false;
}
const format = this._getValue(target, ns.aml.vocabularies.shapes.format);
if (format && typeof format === 'string') {
result.format = format;
}
const multipleOf = this._getValue(target, ns.aml.vocabularies.shapes.multipleOf);
if (typeof multipleOf === 'number') {
result.multipleOf = multipleOf;
}
return result;
}
/**
* @param {SchemaShape} object
* @param {ShapeProcessingOptions=} options
* @returns {ApiSchemaShape}
*/
schemaShape(object, options={}) {
let target = object;
const result = /** @type ApiSchemaShape */ (this.anyShape(target, options));
if (this.isLink(target)) {
const value = /** @type SchemaShape */ (this.getLinkTarget(target));
if (value) {
target = value;
}
}
const { ns } = this;
const mediaType = this._getValue(target, ns.aml.vocabularies.core.mediaType);
if (mediaType && typeof mediaType === 'string') {
result.mediaType = mediaType;
}
const raw = this._getValue(target, ns.aml.vocabularies.document.raw);
if (raw && typeof raw === 'string') {
result.raw = raw;
}
return result;
}
/**
* @param {RecursiveShape} object
* @returns {ApiRecursiveShape}
*/
recursiveShape(object) {
let target = object;
const result = /** @type ApiRecursiveShape */ (this.shape(target));
if (this.isLink(target)) {
const value = /** @type RecursiveShape */ (this.getLinkTarget(target));
if (value) {
target = value;
}
}
const { ns } = this;
const fp = this._getLinkValue(object, ns.aml.vocabularies.shapes.fixPoint);
if (fp && typeof fp === 'string') {
result.fixPoint = fp;
}
return result;
}
/**
* @param {DataArrangeShape} object
* @param {ShapeProcessingOptions=} options
* @returns {ApiDataArrangeShape}
*/
dataArrangeShape(object, options={}) {
let target = object;
const result = /** @type ApiDataArrangeShape */ (this.anyShape(target, options));
if (this.isLink(target)) {
const value = /** @type DataArrangeShape */ (this.getLinkTarget(target));
if (value) {
target = value;
}
}
// const { ns } = this;
// const { minItems, maxItems, uniqueItems } = object;
// if (!minItems.isNull) {
// result.minItems = minItems.value();
// }
// if (!maxItems.isNull) {
// result.maxItems = maxItems.value();
// }
// if (!uniqueItems.isNull) {
// result.uniqueItems = uniqueItems.value();
// }
return result;
}
/**
* @param {ArrayShape} object
* @param {ShapeProcessingOptions=} options
* @returns {ApiArrayShape}
*/
arrayShape(object, options={}) {
let target = object;
const result = /** @type ApiArrayShape */ (this.dataArrangeShape(target, options));
if (this.isLink(target)) {
const value = /** @type ArrayShape */ (this.getLinkTarget(target));
if (value) {
target = value;
}
}
const items = target[this._getAmfKey(this.ns.aml.vocabularies.shapes.items)];
if (Array.isArray(items) && items.length) {
const [item] = items;
result.items = this.unknownShape(item);
}
return result;
}
/**
* @param {TupleShape} object
* @param {ShapeProcessingOptions=} options
* @returns {ApiTupleShape}
*/
tupleShape(object, options) {
let target = object;
const result = /** @type ApiTupleShape */ (this.dataArrangeShape(target, options));
if (this.isLink(target)) {
const value = /** @type TupleShape */ (this.getLinkTarget(target));
if (value) {
target = value;
}
}
const items = target[this._getAmfKey(this.ns.aml.vocabularies.shapes.items)];
const prefix = this._getAmfKey(this.ns.w3.rdfSchema.key);
if (Array.isArray(items) && items.length) {
result.items = [];
items.forEach((item) => {
if (Array.isArray(item)) {
// eslint-disable-next-line no-param-reassign
[item] = item;
}
Object.keys(item).filter(k => k.startsWith(prefix)).forEach((key) => {
let shape = item[key];
if (Array.isArray(shape)) {
[shape] = shape;
}
const value = this.unknownShape(shape);
result.items.push(value);
});
});
} else {
result.items = [];
}
return result;
}
/**
* @param {CreativeWork} object The CreativeWork to serialize.
* @returns {ApiDocumentation} Serialized CreativeWork
*/
documentation(object) {
const result = /** @type ApiDocumentation */ ({
id: object['@id'],
types: this.readTypes(object['@type']),
customDomainProperties: this.customDomainProperties(object),
sourceMaps: this.sourceMap(object),
});
const url = this._getLinkValue(object, this.ns.aml.vocabularies.core.url);
if (url && typeof url === 'string') {
result.url = url;
}
const description = this._getValue(object, this.ns.aml.vocabularies.core.description);
if (description && typeof description === 'string') {
result.description = description;
}
const title = this._getValue(object, this.ns.aml.vocabularies.core.title);
if (title && typeof title === 'string') {
result.title = title;
}
return result;
}
/**
* @param {Example} object The Example to serialize.
* @returns {ApiExample} Serialized Example
*/
example(object) {
this._resolve(object);
const result = /** @type ApiExample */ ({
id: object['@id'],
types: this.readTypes(object['@type']),
customDomainProperties: this.customDomainProperties(object),
sourceMaps: this.sourceMap(object),
strict: false,
});
const { ns } = this;
const strict = this._getValue(object, ns.aml.vocabularies.document.strict);
if (typeof strict === 'boolean') {
result.strict = strict;
}
const name = this._getValue(object, ns.aml.vocabularies.core.name);
if (name && typeof name === 'string') {
result.name = name;
}
const displayName = this._getValue(object, ns.aml.vocabularies.core.displayName);
if (displayName && typeof displayName === 'string') {
result.displayName = displayName;
}
const description = this._getValue(object, ns.aml.vocabularies.core.description);
if (description && typeof description === 'string') {
result.description = description;
}
const raw = this._getValue(object, ns.aml.vocabularies.document.raw);
if (raw && typeof raw === 'string') {
result.value = raw;
}
const location = this._getValue(object, ns.aml.vocabularies.document.location);
if (location && typeof location === 'string') {
result.location = location;
}
// if (!mediaType.isNullOrEmpty) {
// result.mediaType = mediaType.value();
// }
const structuredValue = object[this._getAmfKey(ns.aml.vocabularies.document.structuredValue)];
if (Array.isArray(structuredValue) && structuredValue.length) {
const [value] = structuredValue;
result.structuredValue = this.unknownDataNode(value);
}
return result;
}
/**
* @param {XMLSerializer} object
* @returns {ApiXMLSerializer}
*/
xmlSerializer(object) {
const result = /** @type ApiXMLSerializer */ ({
id: object['@id'],
types: this.readTypes(object['@type']),
customDomainProperties: this.customDomainProperties(object),
sourceMaps: this.sourceMap(object),
});
const { ns } = this;
const xmlAttribute = this._getValue(object, ns.aml.vocabularies.shapes.xmlAttribute);
if (typeof xmlAttribute === 'boolean') {
result.attribute = xmlAttribute;
}
const wrapped = this._getValue(object, ns.aml.vocabularies.shapes.xmlWrapped);
if (typeof wrapped === 'boolean') {
result.wrapped = wrapped;
}
const name = this._getValue(object, ns.aml.vocabularies.shapes.xmlName);
if (name && typeof name === 'string') {
result.name = name;
}
const xmlNs = this._getValue(object, ns.aml.vocabularies.shapes.xmlNamespace);
if (xmlNs && typeof xmlNs === 'string') {
result.namespace = xmlNs;
}
const xmlPrefix = this._getValue(object, ns.aml.vocabularies.shapes.xmlPrefix);
if (xmlPrefix && typeof xmlPrefix === 'string') {
result.prefix = xmlPrefix;
}
return result;
}
/**
* @param {DataNode} object
* @returns {ApiDataNodeUnion}
*/
unknownDataNode(object) {
const types = this.readTypes(object['@type']);
const { ns } = this;
if (types.includes(ns.aml.vocabularies.data.Scalar)) {
return this.scalarNode(/** @type ScalarNode */(object));
}
if (types.includes(ns.aml.vocabularies.data.Object)) {
return this.objectNode(/** @type ObjectNode */(object));
}
if (types.includes(ns.aml.vocabularies.data.Array)) {
return this.arrayNode(/** @type ArrayNode */(object));
}
return undefined;
}
/**
* @param {DataNode} object
* @returns {ApiDataNode}
*/
dataNode(object) {
const result = /** @type ApiDataNode */ ({
id: object['@id'],
types: this.readTypes(object['@type']),
customDomainProperties: this.customDomainProperties(object),
});
const { ns } = this;
const name = this._getValue(object, ns.aml.vocabularies.core.name);
if (name && typeof name === 'string') {
result.name = name;
}
return result;
}
/**
* @param {ScalarNode} object
* @returns {ApiScalarNode}
*/
scalarNode(object) {
const result = /** @type ApiScalarNode */ (this.dataNode(object));
const { ns } = this;
const value = this._getValue(object, ns.aml.vocabularies.data.value);
if (value && typeof value === 'string') {
result.value = value;
}
const dataType = this._getLinkValue(object, ns.w3.shacl.datatype);
if (dataType && typeof dataType === 'string') {
result.dataType = dataType;
}
return result;
}
/**
* @param {ObjectNode} object
* @returns {ApiObjectNode}
*/
objectNode(object) {
const result = /** @type ApiObjectNode */ (this.dataNode(object));
result.properties = {};
const prefix = this.ns.aml.vocabularies.data.toString();
const prefixCompact = `${this._getAmfKey(prefix)}:`;
Object.keys(object).forEach((key) => {
if (key.startsWith(prefix) || key.startsWith(prefixCompact)) {
let value = object[key];
if (Array.isArray(value)) {
[value] = value;
}
const name = key.replace(prefix, '').replace(prefixCompact, '');
result.properties[name] = this.unknownDataNode(value);
}
});
return result;
}
/**
* @param {ArrayNode} object
* @returns {ApiArrayNode}
*/
arrayNode(object) {
const result = /** @type ApiArrayNode */ (this.dataNode(object));
result.members = [];
const members = /** @type DataNode[] */ (this._computePropertyArray(object, this.ns.w3.rdfSchema.member));
if (Array.isArray(members) && members.length) {
result.members = members.map((item) => this.unknownDataNode(item));
}
return result;
}
/**
* Adds the custom domain properties to the currently processed property, a.k.a annotations.
* @param {DomainElement} object
* @returns {ApiCustomDomainProperty[]} The list of custom domain properties.
*/
customDomainProperties(object) {
const result = /** @type ApiCustomDomainProperty[] */ ([]);
const ids = this._getLinkValues(object, this.ns.aml.vocabularies.document.customDomainProperties);
if (Array.isArray(ids) && ids.length) {
ids.forEach((id) => {
const key = `amf://id${id}`;
let value = /** @type DomainElement */ (object[id] || object[key]);
if (!value) {
return;
}
if (Array.isArray(value)) {
[value] = value;
}
const extension = this.unknownDataNode(value);
const name = this._getValue(value, this.ns.aml.vocabularies.core.extensionName);
if (!name || !extension) {
return;
}
const cdp = /** @type ApiCustomDomainProperty */ ({
id: key,
name,
extension,
});
result.push(cdp);
});
}
return result;
}
/**
* @param {EndPoint} object The EndPoint to serialize.
* @returns {ApiEndPoint} Serialized EndPoint
*/
endPoint(object) {
const result = /** @type ApiEndPoint */ ({
id: object['@id'],
types: this.readTypes(object['@type']),
customDomainProperties: this.customDomainProperties(object),
sourceMaps: this.sourceMap(object),
path: '',
operations: [],
parameters: [],
payloads: [],
servers: [],
security: [],
extends: [],
});
const { ns } = this;
const path = this._getValue(object, ns.aml.vocabularies.apiContract.path);
if (path && typeof path === 'string') {
result.path = path;
}
const name = this._getValue(object, ns.aml.vocabularies.core.name);
if (name && typeof name === 'string') {
result.name = name;
}
const description = this._getValue(object, ns.aml.vocabularies.core.description);
if (description && typeof description === 'string') {
result.description = description;
}
const summary = this._getValue(object, ns.aml.vocabularies.core.summary);
if (summary && typeof summary === 'string') {
result.summary = summary;
}
const operations = this[getArrayItems](object, ns.aml.vocabularies.apiContract.supportedOperation);
if (Array.isArray(operations) && operations.length) {
result.operations = operations.map(i => this.operation(/** @type Operation */ (i)));
}
const parameters = this[getArrayItems](object, ns.aml.vocabularies.apiContract.parameter);
if (Array.isArray(parameters) && parameters.length) {
result.parameters = parameters.map(i => this.parameter(i));
}
const payloads = this[getArrayItems](object, ns.aml.vocabularies.apiContract.payload);
if (Array.isArray(payloads) && payloads.length) {
resu