import { ArrayElement, ObjectElement, isStringElement, isArrayElement, isElement, isMemberElement, includesClasses, cloneDeep, toValue } from '@swagger-api/apidom-core'; /** * OpenAPI 3.0.3 specification elements. */ import InfoElement from "../../elements/Info.mjs"; import ContactElement from "../../elements/Contact.mjs"; import LicenseElement from "../../elements/License.mjs"; import PathsElement from "../../elements/Paths.mjs"; import PathItemElement from "../../elements/PathItem.mjs"; import ComponentsElement from "../../elements/Components.mjs"; import ExternalDocumentationElement from "../../elements/ExternalDocumentation.mjs"; import OperationElement from "../../elements/Operation.mjs"; import SchemaElement from "../../elements/Schema.mjs"; import RequestBodyElement from "../../elements/RequestBody.mjs"; import ResponsesElement from "../../elements/Responses.mjs"; import ResponseElement from "../../elements/Response.mjs"; import ServerElement from "../../elements/Server.mjs"; import DiscriminatorElement from "../../elements/Discriminator.mjs"; import XmlElement from "../../elements/Xml.mjs"; import OAuthFlowsElement from "../../elements/OAuthFlows.mjs"; import OAuthFlowElement from "../../elements/OAuthFlow.mjs"; import ServerVariableElement from "../../elements/ServerVariable.mjs"; import ParameterElement from "../../elements/Parameter.mjs"; import ExampleElement from "../../elements/Example.mjs"; import HeaderElement from "../../elements/Header.mjs"; import SecuritySchemeElement from "../../elements/SecurityScheme.mjs"; import LinkElement from "../../elements/Link.mjs"; import CallbackElement from "../../elements/Callback.mjs"; import MediaTypeElement from "../../elements/MediaType.mjs"; import EncodingElement from "../../elements/Encoding.mjs"; import SecurityRequirementElement from "../../elements/SecurityRequirement.mjs"; import TagElement from "../../elements/Tag.mjs"; // non-concrete Elements (NCEs) import ServersElement from "../../elements/nces/Servers.mjs"; import SecurityElement from "../../elements/nces/Security.mjs"; import TagsElement from "../../elements/nces/Tags.mjs"; import ServerVariablesElement from "../../elements/nces/ServerVariables.mjs"; import ComponentsSchemasElement from "../../elements/nces/ComponentsSchemas.mjs"; import ComponentsResponsesElement from "../../elements/nces/ComponentsResponses.mjs"; import ComponentsParametersElement from "../../elements/nces/ComponentsParameters.mjs"; import ComponentsExamplesElement from "../../elements/nces/ComponentsExamples.mjs"; import ComponentsRequestBodiesElement from "../../elements/nces/ComponentsRequestBodies.mjs"; import ComponentsHeadersElement from "../../elements/nces/ComponentsHeaders.mjs"; import ComponentsSecuritySchemesElement from "../../elements/nces/ComponentsSecuritySchemes.mjs"; import ComponentsLinksElement from "../../elements/nces/ComponentsLinks.mjs"; import ComponentsCallbacksElement from "../../elements/nces/ComponentsCallbacks.mjs"; import PathItemServersElement from "../../elements/nces/PathItemServers.mjs"; import PathItemParametersElement from "../../elements/nces/PathItemParameters.mjs"; import OperationParametersElement from "../../elements/nces/OperationParameters.mjs"; import ParameterExamplesElement from "../../elements/nces/ParameterExamples.mjs"; import ParameterContentElement from "../../elements/nces/ParameterContent.mjs"; import OperationTagsElement from "../../elements/nces/OperationTags.mjs"; import OperationCallbacksElement from "../../elements/nces/OperationCallbacks.mjs"; import OperationSecurityElement from "../../elements/nces/OperationSecurity.mjs"; import OperationServersElement from "../../elements/nces/OperationServers.mjs"; import RequestBodyContentElement from "../../elements/nces/RequestBodyContent.mjs"; import MediaTypeExamplesElement from "../../elements/nces/MediaTypeExamples.mjs"; import MediaTypeEncodingElement from "../../elements/nces/MediaTypeEncoding.mjs"; import EncodingHeadersElement from "../../elements/nces/EncodingHeaders.mjs"; import ResponseHeadersElement from "../../elements/nces/ResponseHeaders.mjs"; import ResponseContentElement from "../../elements/nces/ResponseContent.mjs"; import ResponseLinksElement from "../../elements/nces/ResponseLinks.mjs"; import DiscriminatorMappingElement from "../../elements/nces/DiscriminatorMapping.mjs"; import OAuthFlowScopesElement from "../../elements/nces/OAuthFlowScopes.mjs"; import LinkParametersElement from "../../elements/nces/LinkParameters.mjs"; import HeaderExamplesElement from "../../elements/nces/HeaderExamples.mjs"; import HeaderContentElement from "../../elements/nces/HeaderContent.mjs"; import { getNodeType } from "../../traversal/visitor.mjs"; /** * This plugin is specific to YAML 1.2 format, which allows defining key-value pairs * with empty key, empty value, or both. If the value is not provided in YAML format, * this plugin compensates for this missing value with the most appropriate semantic element type. * * https://yaml.org/spec/1.2.2/#72-empty-nodes * * @example * * ```yaml * openapi: 3.0.3 * info: * ``` * Refracting result without this plugin: * * (OpenApi3_0Element * (MemberElement * (StringElement) * (OpenapiElement)) * (MemberElement * (StringElement) * (StringElement)) * * Refracting result with this plugin: * * (OpenApi3_0Element * (MemberElement * (StringElement) * (OpenapiElement)) * (MemberElement * (StringElement) * (InfoElement)) */ const isEmptyElement = element => isStringElement(element) && includesClasses(['yaml-e-node', 'yaml-e-scalar'], element); const schema = { // concrete types handling (CTs) OpenApi3_0Element: { info(...args) { return new InfoElement(...args); }, servers(...args) { return new ServersElement(...args); }, paths(...args) { return new PathsElement(...args); }, components(...args) { return new ComponentsElement(...args); }, security(...args) { return new SecurityElement(...args); }, tags(...args) { return new TagsElement(...args); }, externalDocs(...args) { return new ExternalDocumentationElement(...args); } }, InfoElement: { contact(...args) { return new ContactElement(...args); }, license(...args) { return new LicenseElement(...args); } }, ServerElement: { variables(...args) { return new ServerVariablesElement(...args); } }, ServerVariableElement: { enum(...args) { return new ArrayElement(...args); } }, PathsElement: { '[key: *]': function key(...args) { return new PathItemElement(...args); } }, PathItemElement: { get(...args) { return new OperationElement(...args); }, put(...args) { return new OperationElement(...args); }, post(...args) { return new OperationElement(...args); }, delete(...args) { return new OperationElement(...args); }, options(...args) { return new OperationElement(...args); }, head(...args) { return new OperationElement(...args); }, patch(...args) { return new OperationElement(...args); }, trace(...args) { return new OperationElement(...args); }, servers(...args) { return new PathItemServersElement(...args); }, parameters(...args) { return new PathItemParametersElement(...args); } }, OperationElement: { tags(...args) { return new OperationTagsElement(...args); }, externalDocs(...args) { return new ExternalDocumentationElement(...args); }, parameters(...args) { return new OperationParametersElement(...args); }, requestBody(...args) { return new RequestBodyElement(...args); }, responses(...args) { return new ResponsesElement(...args); }, callbacks(...args) { return new OperationCallbacksElement(...args); }, security(...args) { return new OperationSecurityElement(...args); }, servers(...args) { return new OperationServersElement(...args); } }, ParameterElement: { schema(...args) { return new SchemaElement(...args); }, examples(...args) { return new ParameterExamplesElement(...args); }, content(...args) { return new ParameterContentElement(...args); } }, RequestBodyElement: { content(...args) { return new RequestBodyContentElement(...args); } }, MediaTypeElement: { schema(...args) { return new SchemaElement(...args); }, examples(...args) { return new MediaTypeExamplesElement(...args); }, encoding(...args) { return new MediaTypeEncodingElement(...args); } }, EncodingElement: { headers(...args) { return new EncodingHeadersElement(...args); } }, ResponsesElement: { '[key: *]': function key(...args) { return new ResponseElement(...args); } }, ResponseElement: { headers(...args) { return new ResponseHeadersElement(...args); }, content(...args) { return new ResponseContentElement(...args); }, links(...args) { return new ResponseLinksElement(...args); } }, CallbackElement: { '[key: *]': function key(...args) { return new PathItemElement(...args); } }, LinkElement: { parameters(...args) { return new LinkParametersElement(...args); }, server(...args) { return new ServerElement(...args); } }, HeaderElement: { schema(...args) { return new SchemaElement(...args); }, examples(...args) { return new HeaderExamplesElement(...args); }, content(...args) { return new HeaderContentElement(...args); } }, ComponentsElement: { schemas(...args) { return new ComponentsSchemasElement(...args); }, responses(...args) { return new ComponentsResponsesElement(...args); }, parameters(...args) { return new ComponentsParametersElement(...args); }, examples(...args) { return new ComponentsExamplesElement(...args); }, requestBodies(...args) { return new ComponentsRequestBodiesElement(...args); }, headers(...args) { return new ComponentsHeadersElement(...args); }, securitySchemes(...args) { return new ComponentsSecuritySchemesElement(...args); }, links(...args) { return new ComponentsLinksElement(...args); }, callbacks(...args) { return new ComponentsCallbacksElement(...args); } }, SecurityRequirementElement: { '[key: *]': function key(...args) { return new ArrayElement(...args); } }, TagElement: { externalDocs(...args) { return new ExternalDocumentationElement(...args); } }, SchemaElement: { definitions(...args) { const element = new ObjectElement(...args); element.classes.push('json-schema-definitions'); return element; }, allOf(...args) { const element = new ArrayElement(...args); element.classes.push('json-schema-allOf'); return element; }, anyOf(...args) { const element = new ArrayElement(...args); element.classes.push('json-schema-anyOf'); return element; }, oneOf(...args) { const element = new ArrayElement(...args); element.classes.push('json-schema-oneOf'); return element; }, not(...args) { return new SchemaElement(...args); }, items(...args) { return new SchemaElement(...args); }, properties(...args) { const element = new ObjectElement(...args); element.classes.push('json-schema-properties'); return element; }, patternProperties(...args) { const element = new ObjectElement(...args); element.classes.push('json-schema-patternProperties'); return element; }, additionalProperties(...args) { return new SchemaElement(...args); }, enum(...args) { const element = new ArrayElement(...args); element.classes.push('json-schema-enum'); return element; }, required(...args) { const element = new ArrayElement(...args); element.classes.push('json-schema-required'); return element; }, discriminator(...args) { return new DiscriminatorElement(...args); }, xml(...args) { return new XmlElement(...args); }, externalDocs(...args) { return new ExternalDocumentationElement(...args); } }, DiscriminatorElement: { mapping(...args) { return new DiscriminatorMappingElement(...args); } }, SecuritySchemeElement: { flows(...args) { return new OAuthFlowsElement(...args); } }, OAuthFlowsElement: { implicit(...args) { return new OAuthFlowElement(...args); }, password(...args) { return new OAuthFlowElement(...args); }, clientCredentials(...args) { return new OAuthFlowElement(...args); }, authorizationCode(...args) { return new OAuthFlowElement(...args); } }, OAuthFlowElement: { scopes(...args) { return new OAuthFlowScopesElement(...args); } }, // non-concrete types handling (NCEs) [ServerVariablesElement.primaryClass]: { '[key: *]': function key(...args) { return new ServerVariableElement(...args); } }, [ComponentsSchemasElement.primaryClass]: { '[key: *]': function key(...args) { return new SchemaElement(...args); } }, [ComponentsResponsesElement.primaryClass]: { '[key: *]': function key(...args) { return new ResponseElement(...args); } }, [ComponentsParametersElement.primaryClass]: { '[key: *]': function key(...args) { return new ParameterElement(...args); } }, [ComponentsExamplesElement.primaryClass]: { '[key: *]': function key(...args) { return new ExampleElement(...args); } }, [ComponentsRequestBodiesElement.primaryClass]: { '[key: *]': function key(...args) { return new RequestBodyElement(...args); } }, [ComponentsHeadersElement.primaryClass]: { '[key: *]': function key(...args) { return new HeaderElement(...args); } }, [ComponentsSecuritySchemesElement.primaryClass]: { '[key: *]': function key(...args) { return new SecuritySchemeElement(...args); } }, [ComponentsLinksElement.primaryClass]: { '[key: *]': function key(...args) { return new LinkElement(...args); } }, [ComponentsCallbacksElement.primaryClass]: { '[key: *]': function key(...args) { return new CallbackElement(...args); } }, [OperationCallbacksElement.primaryClass]: { '[key: *]': function key(...args) { return new CallbackElement(...args); } }, [ParameterExamplesElement.primaryClass]: { '[key: *]': function key(...args) { return new ExampleElement(...args); } }, [ParameterContentElement.primaryClass]: { '[key: *]': function key(...args) { return new MediaTypeElement(...args); } }, [RequestBodyContentElement.primaryClass]: { '[key: *]': function key(...args) { return new MediaTypeElement(...args); } }, [MediaTypeExamplesElement.primaryClass]: { '[key: *]': function key(...args) { return new ExampleElement(...args); } }, [MediaTypeEncodingElement.primaryClass]: { '[key: *]': function key(...args) { return new EncodingElement(...args); } }, [EncodingHeadersElement.primaryClass]: { '[key: *]': function key(...args) { return new HeaderElement(...args); } }, [ResponseHeadersElement.primaryClass]: { '[key: *]': function key(...args) { return new HeaderElement(...args); } }, [ResponseContentElement.primaryClass]: { '[key: *]': function key(...args) { return new MediaTypeElement(...args); } }, [ResponseLinksElement.primaryClass]: { '[key: *]': function key(...args) { return new LinkElement(...args); } }, 'json-schema-$defs': { '[key: *]': function key(...args) { return new SchemaElement(...args); } }, 'json-schema-dependentSchemas': { '[key: *]': function key(...args) { return new SchemaElement(...args); } }, 'json-schema-properties': { '[key: *]': function key(...args) { return new SchemaElement(...args); } }, [ServersElement.primaryClass]: { '<*>': function asterisk(...args) { return new ServerElement(...args); } }, [SecurityElement.primaryClass]: { '<*>': function asterisk(...args) { return new SecurityRequirementElement(...args); } }, [TagsElement.primaryClass]: { '<*>': function asterisk(...args) { return new TagElement(...args); } }, [PathItemServersElement.primaryClass]: { '<*>': function asterisk(...args) { return new ServerElement(...args); } }, [PathItemParametersElement.primaryClass]: { '<*>': function asterisk(...args) { return new ParameterElement(...args); } }, [OperationParametersElement.primaryClass]: { '<*>': function asterisk(...args) { return new ParameterElement(...args); } }, [OperationSecurityElement.primaryClass]: { '<*>': function asterisk(...args) { return new SecurityRequirementElement(...args); } }, [OperationServersElement.primaryClass]: { '<*>': function asterisk(...args) { return new ServerElement(...args); } }, 'json-schema-allOf': { '<*>': function asterisk(...args) { return new SchemaElement(...args); } }, 'json-schema-anyOf': { '<*>': function asterisk(...args) { return new SchemaElement(...args); } }, 'json-schema-oneOf': { '<*>': function asterisk(...args) { return new SchemaElement(...args); } }, 'json-schema-prefixItems': { '<*>': function asterisk(...args) { return new SchemaElement(...args); } } }; const findElementFactory = (ancestor, keyName) => { const elementType = getNodeType(ancestor); // @ts-ignore const keyMapping = schema[elementType] || schema[toValue(ancestor.classes.first)]; return typeof keyMapping === 'undefined' ? undefined : Object.prototype.hasOwnProperty.call(keyMapping, '[key: *]') ? keyMapping['[key: *]'] : keyMapping[keyName]; }; const plugin = () => () => ({ visitor: { StringElement(element, key, parent, path, ancestors) { if (!isEmptyElement(element)) return undefined; const lineage = [...ancestors, parent].filter(isElement); const parentElement = lineage[lineage.length - 1]; // @TODO(vladimir.gorej@gmail.com): can be replaced by Array.prototype.at in future let elementFactory; let context; if (isArrayElement(parentElement)) { context = element; elementFactory = findElementFactory(parentElement, '<*>'); } else if (isMemberElement(parentElement)) { context = lineage[lineage.length - 2]; // @TODO(vladimir.gorej@gmail.com): can be replaced by Array.prototype.at in future elementFactory = findElementFactory(context, toValue(parentElement.key)); } // no element factory found if (typeof elementFactory !== 'function') return undefined; return elementFactory.call({ context }, undefined, cloneDeep(element.meta), cloneDeep(element.attributes)); } } }); export default plugin;