UNPKG

@typespec/openapi3

Version:

TypeSpec library for emitting OpenAPI 3.0 and OpenAPI 3.1 from the TypeSpec REST protocol binding and converting OpenAPI3 to TypeSpec

180 lines 7.43 kB
import { ArrayBuilder, ObjectBuilder, Placeholder } from "@typespec/asset-emitter"; import { isArrayModelType, resolveEncodedName, } from "@typespec/compiler"; import { shouldInline } from "@typespec/openapi"; import { reportDiagnostic } from "./lib.js"; export async function resolveXmlModule() { const xml = await tryImportXml(); if (xml === undefined) return undefined; return { attachXmlObjectForScalarOrModel: (program, type, emitObject) => { const isXmlModel = isXmlModelChecker(program, type, []); if (!isXmlModel) { return; } const xmlObject = {}; // Resolve XML name const xmlName = resolveEncodedName(program, type, "application/xml"); if (xmlName !== type.name) { xmlObject.name = xmlName; } // Get and set XML namespace if present const currNs = xml.getNs(program, type); if (currNs) { xmlObject.prefix = currNs.prefix; xmlObject.namespace = currNs.namespace; } // Attach xml schema to emitObject if not empty if (Object.keys(xmlObject).length !== 0) { emitObject.xml = xmlObject; } }, attachXmlObjectForModelProperty: (program, options, prop, emitObject, refSchema) => { if (prop.model === undefined || !isXmlModelChecker(program, prop.model, [])) return; const xmlObject = {}; // Resolve XML name const xmlName = resolveEncodedName(program, prop, "application/xml"); const jsonName = resolveEncodedName(program, prop, "application/json"); if (xmlName !== prop.name && xmlName !== jsonName) { xmlObject.name = xmlName; } // Get and set XML namespace if present const currNs = xml.getNs(program, prop); if (currNs) { xmlObject.prefix = currNs.prefix; xmlObject.namespace = currNs.namespace; } // Set XML attribute if present const isAttribute = xml.isAttribute(program, prop); if (isAttribute) { if (prop.type?.kind === "Model") { reportDiagnostic(program, { code: "xml-attribute-invalid-property-type", format: { name: prop.name }, target: prop, }); emitObject.type = "string"; delete refSchema.items; } xmlObject.attribute = true; } // Handle array wrapping if necessary const hasUnwrappedDecorator = xml.isUnwrapped(program, prop); const isArrayProperty = prop.type?.kind === "Model" && isArrayModelType(program, prop.type); if (!isArrayProperty && hasUnwrappedDecorator) { reportDiagnostic(program, { code: "xml-unwrapped-invalid-property-type", format: { name: prop.name }, target: prop, }); } if (isArrayProperty && refSchema.items && !isAttribute) { const propValue = prop.type.indexer.value; const propXmlName = hasUnwrappedDecorator ? xmlName : resolveEncodedName(program, propValue, "application/xml"); if ((refSchema.items instanceof Placeholder || "$ref" in refSchema.items) && !(propValue && shouldInline(program, propValue))) { const items = new ArrayBuilder(); items.push(refSchema.items); refSchema.items = new ObjectBuilder({ allOf: items, xml: { name: propXmlName }, }); } else { refSchema.items = new ObjectBuilder({ ...refSchema.items, xml: { name: propXmlName }, }); } if (!hasUnwrappedDecorator) { xmlObject.wrapped = true; } } if (!isArrayProperty && !refSchema.type && !isAttribute) { xmlObject.name = xmlName; emitObject.allOf = new ArrayBuilder(); emitObject.allOf.push(refSchema); } if (isAttribute && (refSchema instanceof Placeholder || "$ref" in refSchema) && !(prop.type && shouldInline(program, prop.type)) && prop.type?.kind !== "Model") { emitObject.allOf = new ArrayBuilder(); emitObject.allOf.push(refSchema); } if (isArrayProperty && hasUnwrappedDecorator) { // if wrapped is false, xml.name of the wrapping element is ignored. delete xmlObject.name; } // Attach xml schema to emitObject if not empty if (Object.keys(xmlObject).length !== 0) { emitObject.xml = xmlObject; } }, }; } function isXmlModelChecker(program, model, checked) { if (model.decorators && model.decorators.some((d) => d.definition?.namespace.name === "Xml")) { return true; } const xmlName = resolveEncodedName(program, model, "application/xml"); if (xmlName && xmlName !== model.name) { return true; } if (model.kind === "ModelProperty") { const isArrayProperty = model.type?.kind === "Model" && isArrayModelType(program, model.type); if (isArrayProperty) { const propValue = model.type.indexer.value; if (propValue.kind === "Scalar") { if (isXmlModelChecker(program, propValue, checked)) { return true; } } } else { const propModel = model.type; if (propModel && !checked.includes(propModel.name)) { checked.push(propModel.name); if (isXmlModelChecker(program, propModel, checked)) { return true; } } } } if (model.kind === "Model") { for (const prop of model.properties.values()) { if (isXmlModelChecker(program, prop, checked)) { return true; } if (prop.type?.kind === "Model" && isArrayModelType(program, prop.type)) { const propValue = prop.type.indexer.value; const propModel = propValue; if (propModel && !checked.includes(propModel.name)) { checked.push(propModel.name); if (isXmlModelChecker(program, propModel, checked)) { return true; } } } } } if (model.kind === "Scalar" && model.baseScalar) { if (isXmlModelChecker(program, model.baseScalar, checked)) { return true; } } return false; } async function tryImportXml() { try { const module = await import("@typespec/xml"); return module; } catch { return undefined; } } //# sourceMappingURL=xml-module.js.map