UNPKG

@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
/* 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