diff --git a/cds-plugin.js b/cds-plugin.js new file mode 100644 index 0000000..21ee895 --- /dev/null +++ b/cds-plugin.js @@ -0,0 +1,12 @@ +const cds = require('@sap/cds') + +const { compile, import: openapi } = require('./index') + +if (cds.compile?.to) cds.compile.to.openapi = compile + +if (cds.import?.from) cds.import.from.openapi = async function (filepath, options = {}) { + const src = await cds.utils.read(filepath, 'utf-8') + const csn = openapi.openAPI2csn(src) + options.inputFileKind = 'rest' + return csn +} diff --git a/index.js b/index.js index 19615e2..fce2423 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ const { compileToOpenAPI } = require('./lib/compile'); +const importOpenAPI = require('./lib/import'); module.exports = { - compile: compileToOpenAPI + compile: compileToOpenAPI, + import: importOpenAPI } diff --git a/lib/import/importOpenAPI.js b/lib/import/importOpenAPI.js new file mode 100644 index 0000000..b33d4cd --- /dev/null +++ b/lib/import/importOpenAPI.js @@ -0,0 +1,752 @@ +const { + cdsName, + nameFromPath, + pathAndMethod, + serviceName, +} = require("./utilities"); +const cds = require('@sap/cds'); + +module.exports = { importOpenAPI }; + +// we are not interested (yet) in HEAD, OPTIONS, TRACE +const IS_METHOD = { + delete: true, + get: true, + patch: true, + post: true, + put: true, +}; + +const STANDARD_HEADERS = [ + "accept", + "accept-encoding", + "accept-language", + "authorization", + "content-type", + "if-match", +]; + +function importOpenAPI(input) { + const csn = { + definitions: {}, + meta: { creator: "cds-import-openapi" }, + }; + + const context = { + serviceName: serviceName(cds.cli.options["into-namespace"] + ?? input.info?.title + ?? "TODO.service" + ), + oasVersion: input.openapi || input.swagger, + v3: !!input.openapi, + schemas: input.openapi ? input.components?.schemas : input.definitions, + anonymous: [], + JSON: false, + }; + + //TODO: complain if neither swagger nor openapi3, i.e. context.oasVersion is undefined + const service = { + kind: "service", + "@Capabilities.BatchSupported": false, + "@Capabilities.KeyAsSegmentSupported": true, + }; + if (input.info?.title) service["@Core.Description"] = input.info.title; + if (input.info?.version) service["@Core.SchemaVersion"] = input.info.version; + if (input.info?.description) + service["@Core.LongDescription"] = input.info.description; + csn.definitions[context.serviceName] = service; + + operations(context, csn, input); + + reuseTypes(context, csn); + + anonymousTypes(context, csn); + + return csn; +} + +function reuseTypes(context, csn) { + for (const [name, schema] of Object.entries(context.schemas || {})) { + const typeName = `${context.serviceName}_types.${cdsName(name)}`; + csn.definitions[typeName] = cdsType(context, schema, false, true); + csn.definitions[typeName].kind = "type"; + delete csn.definitions[typeName].notNull; + } +} + +function anonymousTypes(context, csn) { + for (const a of context.anonymous) { + csn.definitions[a.name] = a.type; + csn.definitions[a.name].kind = "type"; + } + if (context.JSON) { + csn.definitions["common.JSON"] = { kind: "type", type: "cds.LargeString" }; + } +} + +function operations(context, csn, input) { + const reuseParameters = context.v3 + ? input.components?.parameters + : input.parameters; + const reuseResponses = context.v3 + ? input.components?.responses + : input.responses; + for (const [path, item] of Object.entries(input.paths || {})) { + const globalParameters = item.parameters || []; + for (const [method, operation] of Object.entries(item)) { + if (!IS_METHOD[method]) continue; + const cdsOperation = { + kind: method === "get" ? "function" : "action", + params: {}, + }; + if (operation.tags) cdsOperation["@Common.Label"] = operation.tags[0]; + if (operation.summary) + cdsOperation["@Core.Description"] = operation.summary; + if (operation.description) + cdsOperation["@Core.LongDescription"] = operation.description; + if (cdsOperation.kind === "action" && method !== "post") + cdsOperation["@openapi.method"] = method.toUpperCase(); + cdsOperation["@openapi.path"] = path; + + let bodyParam; + + for (let param of globalParameters.concat(operation.parameters || [])) { + if (param.$ref) { + // resolve reuse parameter + const expectedPrefix = context.v3 + ? "#/components/parameters/" + : "#/parameters/"; + if (!param.$ref.startsWith(expectedPrefix)) { + throw new Error( + `TODO: unexpected reference ${param.$ref} in parameter for ${method} of ${path}`, + ); + } + param = reuseParameters[param.$ref.substring(expectedPrefix.length)]; + } + + if (param.in === "body") { + bodyParam = param; + continue; + } + if (STANDARD_HEADERS.includes(param.name.toLowerCase())) continue; + + const cdsParam = cdsType( + context, + context.v3 ? param?.schema : param, + false, + false, + true, + ); + if (param.description) cdsParam["@description"] = param.description; + cdsParam["@openapi.in"] = param.in; + + // only add the annotation "@openapi.explode" for true scenario + if (param.explode || param.style === "form") + cdsParam["@openapi.explode"] = true; + if (!param.explode) delete cdsParam["@openapi.explode"]; + + if ( + (param.in === "path" && param.style && param.style !== "simple") || + (param.in === "query" && param.style && param.style !== "form") + ) + cdsParam["@openapi.style"] = param.style; + if (param.in === "query" && param.collectionFormat === "ssv") + cdsParam["@openapi.style"] = "spaceDelimited"; + if (param.in === "query" && param.collectionFormat === "pipes") + cdsParam["@openapi.style"] = "pipeDelimited"; + + if (param.in === "query" && param.allowReserved === true) + cdsParam["@openapi.allowReserved"] = true; + + if (param.required && param.in !== "path") + cdsParam["@openapi.required"] = true; + + if (!param.required) { + delete param.notNull; + if (param.default !== undefined) + cdsParam["default"] = { val: param.default }; + } + const name = cdsName(param.name); + if (name !== param.name) cdsParam["@openapi.name"] = param.name; + cdsOperation.params[name] = cdsParam; + } + + const schema = context.v3 + ? requestBodySchema(context, operation.requestBody, input.components) + : v2Schema(context, bodyParam?.schema, operation.consumes); + if (schema) { + cdsOperation.params.body = cdsType(context, schema); + cdsOperation.params.body["@openapi.in"] = "body"; + } + + if (!operation.responses) + throw new Error( + `TODO: no responses for path ${path}, method ${method}`, + ); + + const successCode = Object.keys(operation.responses || {}).find((r) => + r.startsWith("2"), + ); + if (successCode) { + let response = operation.responses[successCode]; + if (response?.$ref) { + // resolve reuse response + const expectedPrefix = context.v3 + ? "#/components/responses/" + : "#/responses/"; + if (!response.$ref.startsWith(expectedPrefix)) + throw new Error( + `TODO: unexpected reference ${response.$ref} in ${successCode} response for method ${method} of path ${path}`, + ); + else + response = + reuseResponses[response.$ref.substring(expectedPrefix.length)]; + } + const responseSchema = context.v3 + ? v3Schema(context, response) + : v2Schema(context, response.schema, operation.produces); + if (responseSchema) + cdsOperation.returns = cdsType(context, responseSchema); + else if (method === "get") + cdsOperation.returns = { type: "cds.Boolean" }; + } else if (method === "get") + cdsOperation.returns = { type: "cds.Boolean" }; + + const operationName = `${context.serviceName}.${nameFromPath( + path, + method, + )}`; + + if (csn.definitions[operationName]) { + const existing = pathAndMethod(csn.definitions[operationName]); + throw new Error( + `Name collision: same name ${operationName} for method ${method} of path ${path} and method ${existing.method.toLowerCase()} of path ${existing.path + }`, + ); + } + csn.definitions[operationName] = cdsOperation; + } + } +} + +function requestBodySchema(context, requestBody, components) { + while (requestBody?.$ref) { + const expectedPrefix = "#/components/requestBodies/"; + if (!requestBody.$ref.startsWith(expectedPrefix)) { + throw new Error( + `Unexpected request body reference ${requestBody.$ref}`, + ); + } else { + requestBody = + components?.requestBodies[ + requestBody.$ref.substring(expectedPrefix.length) + ]; + } + } + + return v3Schema(context, requestBody); +} + +function v3Schema(_context, body) { + if (!body?.content) return undefined; + + const contentTypes = Object.keys(body.content); + if (contentTypes.includes("application/json")) + return body.content["application/json"].schema; + + if (contentTypes.length === 0) return undefined; + + // if (contentTypes.length > 1) + // context.messages.push({ + // message: `Multiple requestBody content-types not including application/json`, + // input: contentTypes, + // }); + + const contentType = contentTypes[0]; + + return { + $contentType: contentType, + ...body.content[contentType]?.schema, + }; +} + +function v2Schema(_context, schema, contentTypes) { + if ( + !schema || + !contentTypes || + contentTypes.includes("application/json") || + contentTypes.includes("application/json;charset=utf-8") || + contentTypes.includes("application/json;charset=UTF-8") + ) + return schema; + + // take the first content type if there are multiple + const contentType = contentTypes[0]; + + return { + $contentType: contentType, + ...schema, + }; +} + +function cdsType( + context, + schema, + arrayItem = false, + namedType = false, + forParameter = false, +) { + /** @type {any} */ + let type = {}; + let hasIncludes = false; + + if (!arrayItem) if (schema.title) type["@title"] = schema.title; + if (schema.description) type["@description"] = schema.description; + if (schema.$contentType && schema.$contentType !== "*/*") + type["@openapi.contentType"] = schema.$contentType; + + if (schema.$ref) { + const refType = referencedType(context, schema.$ref); + const resolvedSchema = refType.schema?.$ref + ? indirectlyReferencedType(context, refType.schema).schema + : refType.schema; + if (namedType && normalizeSchemaType(resolvedSchema) === "object") { + type.kind = "type"; + type.includes = [refType.name]; + type.elements = {}; + } else { + type.type = refType.name; + if (schema.maxLength) type.length = schema.maxLength; + } + return type; + } + + let schemaType = normalizeSchemaType(schema); + + switch (schemaType) { + case "array": + //TODO: complain if "xml" + if (schema.items) { + const itemsType = cdsType( + context, + schema.items, + true, + false, + forParameter, + ); + const annotations = Object.keys(itemsType).filter((k) => + k.startsWith("@"), + ); + // make anonymous type for item if + // - item has annotation + // - item has default + // - item is itself an array (inline or via $ref) + if (isArrayType(context, schema.items, itemsType)) { + // OData forbids chained array-of: wrap inner array in { value: [...] } struct. + const wrapper = { + elements: { value: itemsType }, + }; + type.items = anonymousType(context, wrapper); + } else if (annotations.length > 0 || itemsType.default) { + type.items = anonymousType(context, itemsType); + } else { + type.items = itemsType; + } + } else { + type = someJSON(context, schema, arrayItem, type); + } + break; + + case "boolean": + type.type = "cds.Boolean"; + addDefault(type, schema, forParameter); + break; + + case "file": + type.type = "cds.String"; + break; + + case "integer": + type.type = "cds.Integer"; + if (schema.format === "int64") type.type = "cds.Integer64"; + addDefault(type, schema, forParameter); + addPrimitiveExample(type, schema); + break; + + case "number": + type.type = "cds.Decimal"; + if (schema.format === "double" || schema.format === "float") + type.type = "cds.Double"; + addDefault(type, schema, forParameter); + addPrimitiveExample(type, schema); + break; + + case "object": + type.elements = {}; + //TODO: discriminator(context, type, schema); + + if (schema.anyOf) { + type["@openapi.anyOf"] = JSON.stringify(schema.anyOf); + type["@open"] = true; + } + if (schema.oneOf) { + type["@openapi.oneOf"] = JSON.stringify(schema.oneOf); + type["@open"] = true; + } + + for (const subSchema of schema.allOf || []) { + if (subSchema.$ref) { + hasIncludes = true; + const refType = referencedType(context, subSchema.$ref); + if (!type.includes) type.includes = []; + type.includes.push(refType.name); + } else if (subSchema.type === "object" || subSchema.properties) { + //TODO: what if subSchema has allOf/...? Better recurse here and "merge" the types? + structElements(context, type, subSchema); + } else { + // should not get here + throw new Error(`Error: object with non-object sub-schema`); + } + } + + structElements(context, type, schema); + + if (!namedType && hasIncludes) { + type = anonymousType(context, type); + } + + break; + + case "string": + switch (schema.format) { + case "binary": + type.type = "cds.LargeBinary"; + break; + case "date": + type.type = "cds.Date"; + break; + case "date-time": + type.type = "cds.Timestamp"; + break; + case "time": + type.type = "cds.Time"; + break; + case "uuid": + type.type = "cds.UUID"; + break; + default: + type.type = "cds.String"; + if (schema.maxLength) type.length = schema.maxLength; + } + if (Array.isArray(schema.enum)) { + type["@assert.range"] = true; + type.enum = Object.fromEntries( + schema.enum + .filter((val) => val !== null) + .map((val) => { + const key = cdsName(val); + return key === val ? [val, {}] : [key, { val }]; + }), + ); + } + if (schema.pattern) type["@assert.format"] = schema.pattern; + addDefault(type, schema, forParameter); + addPrimitiveExample(type, schema); + break; + + case undefined: + schemaType = bestMatchingType(context, schema); + if ( + (!namedType || schemaType !== "object") && + schema.allOf && + schema.allOf.length === 1 && + Object.keys(schema).filter( + (k) => + !["description", "nullable"].includes(k) && !k.startsWith("x-"), + ).length === 1 + ) { + const normalizedSchema = { ...schema.allOf[0] }; + if (schema.description) + normalizedSchema.description = schema.description; + type = cdsType( + context, + normalizedSchema, + arrayItem, + namedType, + forParameter, + ); + break; + } + if ( + schema.allOf && + schema.allOf.length === 2 && + schema.allOf[0].$ref && + !schema.allOf[1].$ref && + normalizeSchemaType(schema.allOf[1]) !== "object" + ) { + const normalizedSchema = Object.assign( + { ...schema.allOf[0] }, + schema.allOf[1], + ); + type = cdsType( + context, + normalizedSchema, + arrayItem, + namedType, + forParameter, + ); + break; + } + + if (schemaType) { + schema.type = schemaType; + type = cdsType(context, schema, arrayItem, namedType, forParameter); + break; + } + + // last resort — fall through to default (someJSON) + type = someJSON(context, schema, arrayItem, type); + break; + + default: + type = someJSON(context, schema, arrayItem, type); + break; + } + return type; +} + +function isArrayType(context, schema, cdsResult) { + if (cdsResult.items) return true; // inline array + if (!schema?.$ref) return false; + const resolved = indirectlyReferencedType(context, schema); + return normalizeSchemaType(resolved.schema) === "array"; +} + +function someJSON(context, schema, arrayItem, type) { + context.JSON = true; + const jsonSchema = minimalSchema(schema); + if (arrayItem && jsonSchema !== "{}") { + type = anonymousType(context, { + type: "common.JSON", + "@openapi.schema": jsonSchema, + }); + } else { + type.type = "common.JSON"; + if (jsonSchema !== "{}") type["@openapi.schema"] = jsonSchema; + } + return type; +} + +function addDefault(type, schema, forParameter) { + if (schema.default !== undefined) type.default = { val: schema.default }; + if (forParameter) return; +} + +function addPrimitiveExample(type, schema) { + const example = schema.examples?.[0] || schema.example; + if (!example || example.$ref) return; + type["@Core.Example.$Type"] = "Core.PrimitiveExampleValue"; + type["@Core.Example.Value"] = example; +} + +function minimalSchema(schema) { + const s = { ...schema }; + delete s.title; + delete s.description; + delete s.$contentType; + return JSON.stringify(s); +} + +function anonymousType(context, type) { + //TODO: construct newName from path leading here instead of using a counter + // pro: makes imported models more stable + // con: may cause name clashes + const newName = `${context.serviceName}.anonymous.type${context.anonymous.length}`; + context.anonymous.push({ name: newName, type: type }); + return { type: newName }; +} + +function bestMatchingType(context, schema) { + if (schema.type) return schema.type; + let type; + if (schema.allOf || schema.anyOf || schema.oneOf) { + const xOf = (schema.allOf || []).concat( + schema.anyOf || [], + schema.oneOf || [], + ); + for (const subSchema of xOf) { + // determine overall type of this construct + if (subSchema.$ref) { + const refType = indirectlyReferencedType(context, subSchema); + if (!refType.schema) return undefined; + + if ( + refType.schema.allOf || + refType.schema.anyOf || + refType.schema.oneOf + ) { + const subType = bestMatchingType(context, refType.schema); + type = betterType(type, { type: subType }); + } else type = betterType(type, refType.schema); + } else { + //TODO: nested xOf - not yet encountered + type = betterType(type, subSchema); + } + } + } + return type; +} + +function indirectlyReferencedType(context, subSchema) { + let refType = { schema: subSchema }; + let limit = 10; + while (refType.schema?.$ref && limit-- > 0) + refType = referencedType(context, refType.schema.$ref); + return refType; +} + +function referencedType(context, ref) { + const expectedPrefix = context.v3 + ? "#/components/schemas/" + : "#/definitions/"; + if (!ref.startsWith(expectedPrefix)) { + throw new Error(`TODO: unexpected reference ${ref} in schema.`); + } + const schemaName = decodeURIComponent(ref.substring(expectedPrefix.length)); + const schema = context.schemas[schemaName]; + + return { + name: `${context.serviceName}_types.${cdsName(schemaName)}`, + schema, + }; +} + +function normalizeSchemaType(schema) { + if (!schema) return undefined; + if (!schema.type && schema.items) return "array"; + if (!schema.type && schema.maxLength) return "string"; + if (!schema.type && schema.pattern) return "string"; + + if (!schema.type && schema.enum?.every((v) => typeof v === "string")) + return "string"; + + if ( + !schema.type && + (schema.properties || + schema.patternProperties || + schema.additionalProperties || + schema.discriminator) + ) + return "object"; + + let schemaType = schema.type; + if (Array.isArray(schemaType)) { + schemaType = schemaType.sort(); + if (schemaType.includes("null")) { + const index = schemaType.indexOf("null"); + schemaType.splice(index, 1); + } + if (schemaType.length === 1) schemaType = schemaType[0]; + if ( + schemaType.length === 2 && + schemaType[0] === "integer" && + schemaType[1] === "number" + ) + return "number"; + } + return schemaType; +} + +//TODO: better name :-) +function betterType(currentType, schema) { + const schemaType = normalizeSchemaType(schema); + if (currentType === undefined) return schemaType; + if (currentType === schemaType) return currentType; + if (currentType === "string" && ["integer", "number"].includes(schemaType)) + return "string"; + if (["integer", "number"].includes(currentType) && schemaType === "string") + return "string"; + //TODO: more cases here? + return null; +} + +// function discriminator(context, type, schema) { +// if (!schema.discriminator) return; +// console.log("hi"); +// const propertyName = +// schema.discriminator.propertyName || schema.discriminator; +// type["@openapi.discriminator"] = { propertyName }; +// if (schema.discriminator.mapping) { +// const mapping = {}; +// for (const [value, reference] of Object.entries( +// schema.discriminator.mapping +// )) { +// mapping[value] = referencedType(context, reference).name; +// } +// type["@openapi.discriminator"].mapping = mapping; +// } +// } + +function structElements(context, type, schema) { + checkCircularReference(schema); + const schemaName = Object.keys(context.schemas || {}).find( + (key) => context.schemas[key] === schema, + ); + const refPath = `#/components/schemas/${schemaName}`; + //TODO: interpret "required" + for (const [prop, propSchema] of Object.entries(schema.properties || {})) { + const name = cdsName(prop); + //to detect recursive data types + if (propSchema?.$ref === refPath || propSchema?.items?.$ref === refPath) { + console.warn( + `Recursive data type detected: ${schemaName} for the property ${name}`, + ); + } + //TODO: check if property "name" already exists and has identical cdsType, warn otherwise + type.elements[name] = cdsType(context, propSchema); + if (name !== prop) type.elements[name]["@openapi.name"] = prop; + // Add @mandatory if the property is in the required array + if (schema.required && schema.required.includes(prop)) { + type.elements[name]["@mandatory"] = true; + } + } +} + +// function to check circular references and throw error if found +function checkCircularReference(schema, visited = new Set()) { + if (schema.$ref) { + const ref = schema.$ref; + if (visited.has(ref)) { + console.warn(`Circular reference detected: ${ref}`); + } + visited.add(ref); + } + + if (schema.allOf) { + schema.allOf.forEach((subSchema) => + checkCircularReference(subSchema, visited), + ); + } + + if (schema.anyOf) { + schema.anyOf.forEach((subSchema) => + checkCircularReference(subSchema, visited), + ); + } + + if (schema.oneOf) { + schema.oneOf.forEach((subSchema) => + checkCircularReference(subSchema, visited), + ); + } + + if (schema.properties) { + Object.values(schema.properties).forEach((subSchema) => + checkCircularReference(subSchema, visited), + ); + } + + if (schema.items) { + checkCircularReference(schema.items, visited); + } +} diff --git a/lib/import/index.js b/lib/import/index.js new file mode 100644 index 0000000..55f6a04 --- /dev/null +++ b/lib/import/index.js @@ -0,0 +1,20 @@ +"use strict" + +const { importOpenAPI } = require("./importOpenAPI"); + +const INVALID_OPENAPI_FILE = 'The OpenAPI file is not valid. Specify the correct OpenAPI file.'; + +function openAPI2csn(source) { + let csn = {}; + try { + const fileContentToJSON = JSON.parse(source); + csn = importOpenAPI(fileContentToJSON); + } catch { + throw new Error(INVALID_OPENAPI_FILE); + } + return csn; +} + +module.exports = { + openAPI2csn +} diff --git a/lib/import/utilities.js b/lib/import/utilities.js new file mode 100644 index 0000000..646ac85 --- /dev/null +++ b/lib/import/utilities.js @@ -0,0 +1,43 @@ +module.exports = { cdsName, nameFromPath, pathAndMethod, serviceName }; + +function cdsName(oasName) { + if (oasName === "") return "_"; + if (typeof oasName === "number") return `_${JSON.stringify(oasName)}`; + let name = oasName; + if ( + !name.startsWith("_") && + name.match(/^(\p{Nd}|\p{Mn}|\p{Mc}|\p{Pc}|\p{Cf})/u) + ) + name = "_" + name; + return name.replace(/[^_\p{L}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]/gu, "_"); + //TODO: shorten if longer than 128 characters +} + +function nameFromPath(path, method) { + let cleanPath = path + .substring(1) + .replace(/\/$/, "") + .replace(/{[^}]+}/g, ".") + .replace(/\/\./g, "_") + .replace(/[^_\p{L}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]/gu, "_"); + if (cleanPath === "") cleanPath = "_root"; + //TODO: shorten if longer than 128 characters + return method === "get" ? cleanPath : `${cleanPath}_${method}`; +} + +function pathAndMethod(operation) { + const path = operation["@openapi.path"]; + const method = + operation.kind === "function" + ? "GET" + : operation["@openapi.method"] || "POST"; + return { path, method }; +} + +function serviceName(title) { + return title + .trim() + .replace(/(\s|-)+/g, ".") + .replace(/[^._\p{L}\p{Nl}\p{Nd}\p{Mn}\p{Mc}\p{Pc}\p{Cf}]/gu, "_"); + //TODO: shorten if longer than 511-129=382 characters +} diff --git a/package.json b/package.json index 7021f02..78a0e7f 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,21 @@ "main": "index.js", "files": [ "lib/", + "index.js", + "cds-plugin.js", "LICENSE" ], + "cds": { + "plugins": [ + "@cap-js/openapi" + ], + "schema": { + "importFrom": { + "const": "openapi", + "description": "OpenAPI specification" + } + } + }, "scripts": { "test": "node --test", "lint": "npx eslint .", diff --git a/test/lib/import/importOpenAPI.test.js b/test/lib/import/importOpenAPI.test.js new file mode 100644 index 0000000..d23b399 --- /dev/null +++ b/test/lib/import/importOpenAPI.test.js @@ -0,0 +1,594 @@ +const { describe, it } = require('node:test') +const assert = require('node:assert') +const fs = require('fs') +const path = require('path') + +const { importOpenAPI } = require('../../../lib/import/importOpenAPI') +const { openAPI2csn } = require('../../../lib/import') + +const base = { + openapi: '3.0.0', + info: { title: 'Test', version: '1.0' }, + paths: {} +} + +function make(paths, schemas) { + return { ...base, paths, components: schemas ? { schemas } : undefined } +} + +describe('Import examples', () => { + it('petstore Swagger', () => { + const swagger = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'input/petstore.swagger.json'))) + const expected = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'output/petstore.swagger.csn'))) + const csn = importOpenAPI(swagger) + assert.deepStrictEqual(csn, expected, 'imported CSN') + }) + + it('wizard-world OpenAPI3', () => { + const openapi = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'input/wizard-world.json'))) + const expected = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'output/wizard-world.csn'))) + const csn = importOpenAPI(openapi) + assert.deepStrictEqual(csn, expected, 'imported CSN') + }) + + it('Circular Reference OpenAPI3', () => { + const openapi = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'input/ref.json'))) + const expected = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'output/ref.csn'))) + const csn = importOpenAPI(openapi) + assert.deepStrictEqual(csn, expected, 'imported CSN') + }) + + it('petstore OpenAPI3 via openAPI2csn', () => { + const src = fs.readFileSync(path.resolve(__dirname, 'input/petstore.json'), 'utf-8') + const expected = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'output/petstore.csn'))) + const csn = openAPI2csn(src) + assert.deepStrictEqual(csn, expected, 'imported CSN') + }) +}) + +describe('Import edge cases', () => { + it('empty input', () => { + const csn = importOpenAPI({}) + assert.ok(csn.definitions) + }) + + it('invalid JSON throws', () => { + assert.throws(() => openAPI2csn('not json'), /not valid/) + }) + + // common.JSON — schema with no recognized type + it('untyped schema produces common.JSON', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '200': { content: { 'application/json': { schema: { not: { type: 'string' } } } } } } } } + })) + assert.ok(csn.definitions['common.JSON']) + }) + + // array with no items => someJSON + it('array with no items produces common.JSON', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '200': { content: { 'application/json': { schema: { type: 'array' } } } } } } } + })) + assert.ok(csn.definitions['common.JSON']) + }) + + // array-of-array => anonymous wrapper type + it('array of array items wrapped in anonymous type', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '200': { content: { 'application/json': { schema: { type: 'array', items: { type: 'array', items: { type: 'string' } } } } } } } } } + })) + const anon = Object.keys(csn.definitions).find(k => k.includes('anonymous')) + assert.ok(anon) + }) + + // array items with annotation => anonymous type + it('array items with description wrapped in anonymous type', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '200': { content: { 'application/json': { schema: { type: 'array', items: { type: 'string', description: 'a string' } } } } } } } } + })) + const anon = Object.keys(csn.definitions).find(k => k.includes('anonymous')) + assert.ok(anon) + }) + + // boolean type + it('boolean parameter', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'flag', in: 'query', schema: { type: 'boolean', default: true } }], responses: { '204': {} } } } + })) + const op = csn.definitions['Test.foo'] + assert.equal(op.params.flag.type, 'cds.Boolean') + }) + + // number type with double format + it('number double format', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'n', in: 'query', schema: { type: 'number', format: 'double' } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.n.type, 'cds.Double') + }) + + // integer int64 + it('integer int64 format', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'n', in: 'query', schema: { type: 'integer', format: 'int64' } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.n.type, 'cds.Integer64') + }) + + // string formats + it('string binary format', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 's', in: 'query', schema: { type: 'string', format: 'binary' } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.s.type, 'cds.LargeBinary') + }) + + it('string date format', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 's', in: 'query', schema: { type: 'string', format: 'date' } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.s.type, 'cds.Date') + }) + + it('string date-time format', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 's', in: 'query', schema: { type: 'string', format: 'date-time' } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.s.type, 'cds.Timestamp') + }) + + it('string time format', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 's', in: 'query', schema: { type: 'string', format: 'time' } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.s.type, 'cds.Time') + }) + + it('string uuid format', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 's', in: 'query', schema: { type: 'string', format: 'uuid' } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.s.type, 'cds.UUID') + }) + + it('string file format', () => { + const csn = importOpenAPI({ + swagger: '2.0', + info: { title: 'Test', version: '1.0' }, + paths: { '/foo': { get: { parameters: [{ name: 's', in: 'query', type: 'file' }], responses: { '204': {} } } } } + }) + assert.equal(csn.definitions['Test.foo'].params.s.type, 'cds.String') + }) + + // string enum + it('string enum', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 's', in: 'query', schema: { type: 'string', enum: ['a', 'b', null] } }], responses: { '204': {} } } } + })) + assert.ok(csn.definitions['Test.foo'].params.s.enum) + }) + + // string pattern + it('string pattern', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 's', in: 'query', schema: { type: 'string', pattern: '^[a-z]+$' } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.s['@assert.format'], '^[a-z]+$') + }) + + // object with anyOf / oneOf + it('object with anyOf', () => { + const csn = importOpenAPI(make({ + '/foo': { post: { requestBody: { content: { 'application/json': { schema: { type: 'object', anyOf: [{ type: 'string' }] } } } }, responses: { '204': {} } } } + })) + assert.ok(csn.definitions['Test.foo_post'].params.body['@openapi.anyOf']) + }) + + it('object with oneOf', () => { + const csn = importOpenAPI(make({ + '/foo': { post: { requestBody: { content: { 'application/json': { schema: { type: 'object', oneOf: [{ type: 'string' }] } } } }, responses: { '204': {} } } } + })) + assert.ok(csn.definitions['Test.foo_post'].params.body['@openapi.oneOf']) + }) + + // non-JSON content type in response + it('non-JSON response content type', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '200': { content: { 'text/plain': { schema: { type: 'string' } } } } } } } + })) + assert.ok(csn.definitions['Test.foo'].returns['@openapi.contentType']) + }) + + // v2 non-JSON content type in response + it('v2 non-JSON produces/consumes content type', () => { + const csn = importOpenAPI({ + swagger: '2.0', + info: { title: 'Test', version: '1.0' }, + paths: { + '/foo': { get: { produces: ['text/plain'], responses: { '200': { schema: { type: 'string' } } } } } + } + }) + assert.ok(csn.definitions['Test.foo'].returns['@openapi.contentType']) + }) + + // v2 body parameter + it('v2 body parameter', () => { + const csn = importOpenAPI({ + swagger: '2.0', + info: { title: 'Test', version: '1.0' }, + paths: { + '/foo': { post: { consumes: ['application/json'], parameters: [{ name: 'body', in: 'body', schema: { type: 'object', properties: { x: { type: 'string' } } } }], responses: { '204': {} } } } + } + }) + assert.ok(csn.definitions['Test.foo_post'].params.body) + }) + + // requestBody with $ref + it('requestBody with $ref', () => { + const csn = importOpenAPI({ + ...base, + paths: { + '/foo': { post: { requestBody: { $ref: '#/components/requestBodies/MyBody' }, responses: { '204': {} } } } + }, + components: { + requestBodies: { MyBody: { content: { 'application/json': { schema: { type: 'object', properties: { x: { type: 'string' } } } } } } } + } + }) + assert.ok(csn.definitions['Test.foo_post'].params.body) + }) + + // reuse parameter via $ref + it('reuse parameter via $ref', () => { + const csn = importOpenAPI({ + ...base, + paths: { + '/foo': { get: { parameters: [{ $ref: '#/components/parameters/MyParam' }], responses: { '204': {} } } } + }, + components: { + parameters: { MyParam: { name: 'myParam', in: 'query', schema: { type: 'string' } } } + } + }) + assert.ok(csn.definitions['Test.foo'].params.myParam) + }) + + // reuse response via $ref + it('reuse response via $ref', () => { + const csn = importOpenAPI({ + ...base, + paths: { + '/foo': { get: { responses: { '200': { $ref: '#/components/responses/MyResponse' } } } } + }, + components: { + responses: { MyResponse: { content: { 'application/json': { schema: { type: 'string' } } } } } + } + }) + assert.equal(csn.definitions['Test.foo'].returns.type, 'cds.String') + }) + + // GET with no 2xx response => cds.Boolean + it('GET with no success response returns cds.Boolean', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '400': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].returns.type, 'cds.Boolean') + }) + + // GET with 2xx but no schema => cds.Boolean + it('GET with 2xx but no schema returns cds.Boolean', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].returns.type, 'cds.Boolean') + }) + + // name collision throws + it('name collision throws', () => { + assert.throws(() => importOpenAPI(make({ + '/foo': { + get: { responses: { '204': {} } }, + put: { responses: { '204': {} } } // nameFromPath('/foo','put') collides with a manually crafted collision + }, + '/foo_put': { get: { responses: { '204': {} } } } + })), /Name collision/) + }) + + // $ref with unexpected prefix throws + it('unexpected schema $ref throws', () => { + assert.throws(() => importOpenAPI(make( + { '/foo': { get: { responses: { '200': { content: { 'application/json': { schema: { $ref: '#/other/Foo' } } } } } } } }, + { Foo: { type: 'object' } } + )), /unexpected reference/) + }) + + // unexpected parameter $ref throws + it('unexpected parameter $ref throws', () => { + assert.throws(() => importOpenAPI({ + ...base, + paths: { + '/foo': { get: { parameters: [{ $ref: '#/other/MyParam' }], responses: { '204': {} } } } + }, + components: { parameters: {} } + }), /unexpected reference/) + }) + + // no responses throws + it('operation with no responses throws', () => { + assert.throws(() => importOpenAPI(make({ + '/foo': { get: {} } + })), /no responses/) + }) + + // allOf single-element normalization + it('allOf with single element normalizes type', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'p', in: 'query', schema: { allOf: [{ type: 'string' }] } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.p.type, 'cds.String') + }) + + // allOf two-element with $ref normalization — merges ref with constraint, returns ref type + it('allOf two-element $ref + primitive', () => { + const csn = importOpenAPI(make( + { '/foo': { get: { parameters: [{ name: 'p', in: 'query', schema: { allOf: [{ $ref: '#/components/schemas/MyStr' }, { maxLength: 10 }] } }], responses: { '204': {} } } } }, + { MyStr: { type: 'string' } } + )) + assert.equal(csn.definitions['Test.foo'].params.p.type, 'Test_types.MyStr') + }) + + // normalizeSchemaType: array type with null + it('nullable array type normalizes', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'p', in: 'query', schema: { type: ['string', 'null'] } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.p.type, 'cds.String') + }) + + // normalizeSchemaType: [integer, number] => number + it('type [integer, number] normalizes to number', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'p', in: 'query', schema: { type: ['integer', 'number'] } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.p.type, 'cds.Decimal') + }) + + // openapi.explode and openapi.style + it('explode and style annotations', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'p', in: 'query', style: 'form', explode: true, schema: { type: 'string' } }], responses: { '204': {} } } } + })) + assert.ok(csn.definitions['Test.foo'].params.p['@openapi.explode']) + }) + + it('spaceDelimited style annotation', () => { + const csn = importOpenAPI({ + swagger: '2.0', + info: { title: 'Test', version: '1.0' }, + paths: { '/foo': { get: { parameters: [{ name: 'p', in: 'query', type: 'string', collectionFormat: 'ssv' }], responses: { '204': {} } } } } + }) + assert.equal(csn.definitions['Test.foo'].params.p['@openapi.style'], 'spaceDelimited') + }) + + it('pipeDelimited style annotation', () => { + const csn = importOpenAPI({ + swagger: '2.0', + info: { title: 'Test', version: '1.0' }, + paths: { '/foo': { get: { parameters: [{ name: 'p', in: 'query', type: 'string', collectionFormat: 'pipes' }], responses: { '204': {} } } } } + }) + assert.equal(csn.definitions['Test.foo'].params.p['@openapi.style'], 'pipeDelimited') + }) + + it('allowReserved annotation', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'p', in: 'query', allowReserved: true, schema: { type: 'string' } }], responses: { '204': {} } } } + })) + assert.ok(csn.definitions['Test.foo'].params.p['@openapi.allowReserved']) + }) + + // required parameter annotation + it('required query parameter', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'p', in: 'query', required: true, schema: { type: 'string' } }], responses: { '204': {} } } } + })) + assert.ok(csn.definitions['Test.foo'].params.p['@openapi.required']) + }) + + // schema required array => @mandatory + it('required property gets @mandatory', () => { + const csn = importOpenAPI(make({}, { + MyType: { type: 'object', required: ['name'], properties: { name: { type: 'string' } } } + })) + assert.ok(csn.definitions['Test_types.MyType'].elements.name['@mandatory']) + }) + + // pathAndMethod: action with @openapi.method + it('pathAndMethod for non-POST action', () => { + const csn = importOpenAPI(make({ + '/foo': { put: { responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo_put']['@openapi.method'], 'PUT') + }) + + // example annotation + it('primitive example annotation', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'p', in: 'query', schema: { type: 'integer', example: 42 } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.p['@Core.Example.Value'], 42) + }) + + // namedType $ref to object => includes + it('named type $ref to object uses includes', () => { + const csn = importOpenAPI(make({}, { + Base: { type: 'object', properties: { x: { type: 'string' } } }, + Child: { $ref: '#/components/schemas/Base' } + })) + assert.ok(csn.definitions['Test_types.Child'].includes) + }) + + // object allOf with $ref subschema => hasIncludes + anonymousType (lines 399-402, 415-416) + it('object allOf with $ref produces anonymous includes type', () => { + const csn = importOpenAPI(make( + { '/foo': { post: { requestBody: { content: { 'application/json': { schema: { type: 'object', allOf: [{ $ref: '#/components/schemas/Base' }], properties: { extra: { type: 'string' } } } } } }, responses: { '204': {} } } } }, + { Base: { type: 'object', properties: { id: { type: 'string' } } } } + )) + const anon = Object.keys(csn.definitions).find(k => k.includes('anonymous')) + assert.ok(anon) + assert.ok(csn.definitions[anon].includes) + }) + + // object allOf with subSchema.properties (line 404-405) + it('object allOf with inline properties subschema', () => { + const csn = importOpenAPI(make( + { '/foo': { post: { requestBody: { content: { 'application/json': { schema: { type: 'object', allOf: [{ properties: { x: { type: 'string' } } }] } } } }, responses: { '204': {} } } } }, + {} + )) + assert.ok(csn.definitions['Test.foo_post'].params.body) + }) + + it('conflicting types in allOf falls back to someJSON', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '200': { content: { 'application/json': { schema: { allOf: [{ type: 'integer' }, { type: 'boolean' }] } } } } } } } + })) + assert.ok(csn.definitions['common.JSON']) + }) + + // someJSON with arrayItem and non-empty schema (line 529) + it('someJSON as array item produces anonymous type with schema', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '200': { content: { 'application/json': { schema: { type: 'array', items: { allOf: [{ type: 'integer' }, { type: 'boolean' }] } } } } } } } } + })) + const anon = Object.values(csn.definitions).find(d => d['@openapi.schema']) + assert.ok(anon) + }) + + // unexpected response $ref prefix throws (line 199) + it('unexpected response $ref prefix throws', () => { + assert.throws(() => importOpenAPI({ + ...base, + paths: { '/foo': { get: { responses: { '200': { $ref: '#/other/MyResponse' } } } } }, + components: { responses: {} } + }), /unexpected reference/) + }) + + // unexpected requestBody $ref prefix throws (line 236) + it('unexpected requestBody $ref prefix throws', () => { + assert.throws(() => importOpenAPI({ + ...base, + paths: { '/foo': { post: { requestBody: { $ref: '#/other/MyBody' }, responses: { '204': {} } } } }, + components: { requestBodies: {} } + }), /Unexpected request body reference/) + }) + + // allOf with non-object sub-schema throws (line 408) + it('allOf with non-object sub-schema throws', () => { + assert.throws(() => importOpenAPI(make({ + '/foo': { post: { requestBody: { content: { 'application/json': { schema: { type: 'object', allOf: [{ type: 'string' }] } } } }, responses: { '204': {} } } } + })), /non-object sub-schema/) + }) + + // betterType returns null — conflicting non-string types (line 670) + it('betterType with incompatible types returns null => someJSON', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '200': { content: { 'application/json': { schema: { anyOf: [{ type: 'integer' }, { type: 'boolean' }] } } } } } } } + })) + assert.ok(csn.definitions['common.JSON']) + }) + + // recursive type detection via items.$ref (line 701) + it('recursive items.$ref logs warning', () => { + const warnings = [] + const orig = console.warn + console.warn = (...args) => warnings.push(args.join(' ')) + importOpenAPI(make({}, { + Node: { type: 'object', properties: { children: { type: 'array', items: { $ref: '#/components/schemas/Node' } } } } + })) + console.warn = orig + assert.ok(warnings.some(w => w.includes('Recursive'))) + }) + + // bestMatchingType via allOf $ref with xOf on referenced schema + // standard header param is skipped (line 131) + it('standard header parameter is skipped', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'Authorization', in: 'header', schema: { type: 'string' } }], responses: { '204': {} } } } + })) + assert.ok(!csn.definitions['Test.foo'].params?.Authorization) + }) + + // string with maxLength (line 439) + it('string with maxLength', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 's', in: 'query', schema: { type: 'string', maxLength: 50 } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.s.length, 50) + }) + + // allOf single-element with description on parent (line 470) + it('allOf single-element with description inherits description', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'p', in: 'query', schema: { description: 'my desc', allOf: [{ type: 'string' }] } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.p['@description'], 'my desc') + }) + + // examples array (line 546) + it('examples array annotation', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'p', in: 'query', schema: { type: 'integer', examples: [99] } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo'].params.p['@Core.Example.Value'], 99) + }) + + // betterType: anyOf string + integer => string (lines 665-666) + it('anyOf string and integer resolves to string', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { responses: { '200': { content: { 'application/json': { schema: { anyOf: [{ type: 'string' }, { type: 'integer' }] } } } } } } } + })) + assert.equal(csn.definitions['Test.foo'].returns.type, 'cds.String') + }) + + // v2 parameter reuse via #/parameters/ (line 118) + it('v2 parameter reuse via $ref', () => { + const csn = importOpenAPI({ + swagger: '2.0', + info: { title: 'Test', version: '1.0' }, + parameters: { MyParam: { name: 'myParam', in: 'query', type: 'string' } }, + paths: { '/foo': { get: { parameters: [{ $ref: '#/parameters/MyParam' }], responses: { '204': {} } } } } + }) + assert.ok(csn.definitions['Test.foo'].params.myParam) + }) + + // v2 response reuse via #/responses/ (line 196) + it('v2 response reuse via $ref', () => { + const csn = importOpenAPI({ + swagger: '2.0', + info: { title: 'Test', version: '1.0' }, + responses: { MyResponse: { schema: { type: 'string' } } }, + paths: { '/foo': { get: { responses: { '200': { $ref: '#/responses/MyResponse' } } } } } + }) + assert.equal(csn.definitions['Test.foo'].returns.type, 'cds.String') + }) + + // path parameter with non-simple style (lines 149, 152) + it('path parameter with non-simple style gets @openapi.style', () => { + const csn = importOpenAPI(make({ + '/foo/{id}': { get: { parameters: [{ name: 'id', in: 'path', style: 'matrix', schema: { type: 'string' } }], responses: { '204': {} } } } + })) + assert.equal(csn.definitions['Test.foo_'].params.id['@openapi.style'], 'matrix') + }) + + + it('schema type null hits default case => common.JSON', () => { + const csn = importOpenAPI(make({}, { Null: { type: 'null' } })) + assert.ok(csn.definitions['common.JSON']) + }) + + it('bestMatchingType via allOf with xOf-typed ref', () => { + const csn = importOpenAPI(make({ + '/foo': { get: { parameters: [{ name: 'p', in: 'query', schema: { allOf: [{ $ref: '#/components/schemas/MyUnion' }] } }], responses: { '204': {} } } } + }, { + MyUnion: { anyOf: [{ type: 'string' }, { type: 'string' }] } + })) + assert.ok(csn.definitions['Test.foo'].params.p) + }) +}) diff --git a/test/lib/import/input/petstore.json b/test/lib/import/input/petstore.json new file mode 100644 index 0000000..66ffbc6 --- /dev/null +++ b/test/lib/import/input/petstore.json @@ -0,0 +1,178 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "license": { + "name": "MIT" + } + }, + "servers": [ + { + "url": "http://petstore.swagger.io/v1" + } + ], + "paths": { + "/pets": { + "get": { + "summary": "List all pets", + "operationId": "listPets", + "tags": [ + "pets" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at one time (max 100)", + "required": false, + "schema": { + "type": "integer", + "format": "int32" + }, + "default": "213" + } + ], + "responses": { + "200": { + "description": "A paged array of pets", + "headers": { + "x-next": { + "description": "A link to the next page of responses", + "schema": { + "type": "string" + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pets" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "post": { + "summary": "Create a pet", + "operationId": "createPets", + "tags": [ + "pets" + ], + "responses": { + "201": { + "description": "Null response" + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, + "/pets/{petId}": { + "get": { + "summary": "Info for a specific pet", + "operationId": "showPetById", + "tags": [ + "pets" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "required": true, + "description": "The id of the pet to retrieve", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "default": { + "description": "unexpected error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Pet": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "Pets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + }, + "Error": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "message": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/test/lib/import/input/petstore.swagger.json b/test/lib/import/input/petstore.swagger.json new file mode 100644 index 0000000..e1edb70 --- /dev/null +++ b/test/lib/import/input/petstore.swagger.json @@ -0,0 +1,998 @@ +{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.", + "version": "1.0.6", + "title": "Swagger Petstore" + }, + "host": "petstore.swagger.io", + "basePath": "/v2", + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders" + }, + { + "name": "user", + "description": "Operations about user", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "schemes": [ + "https", + "http" + ], + "paths": { + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "additionalMetadata", + "in": "formData", + "description": "Additional data to pass to server", + "required": false, + "default": "32", + "type": "string" + }, + { + "name": "file", + "in": "formData", + "description": "file to upload", + "required": false, + "default": "Empty", + "type": "file" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/ApiResponse" + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet": { + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "", + "operationId": "addPet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "", + "operationId": "updatePet", + "consumes": [ + "application/json", + "application/xml" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Pet object that needs to be added to the store", + "required": true, + "schema": { + "$ref": "#/definitions/Pet" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": true, + "type": "array", + "items": { + "type": "string", + "enum": [ + "available", + "pending", + "sold" + ], + "default": "available" + }, + "collectionFormat": "multi" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Pet" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "type": "integer", + "format": "int64" + }, + { + "name": "name", + "in": "formData", + "description": "Updated name of the pet", + "required": false, + "type": "string" + }, + { + "name": "status", + "in": "formData", + "description": "Updated status of the pet", + "required": false, + "type": "string" + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "api_key", + "in": "header", + "required": false, + "type": "string" + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "", + "operationId": "placeOrder", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "order placed for purchasing the pet", + "required": true, + "schema": { + "$ref": "#/definitions/Order" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid Order" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "operationId": "getOrderById", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of pet that needs to be fetched", + "required": true, + "type": "integer", + "maximum": 10, + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Order" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "operationId": "deleteOrder", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "type": "integer", + "minimum": 1, + "format": "int64" + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "produces": [ + "application/json" + ], + "parameters": [], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/user/createWithArray": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithArrayInput", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "", + "operationId": "createUsersWithListInput", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "List of user object", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/User" + } + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/User" + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Updated user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be updated", + "required": true, + "type": "string" + }, + { + "in": "body", + "name": "body", + "description": "Updated user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "400": { + "description": "Invalid user supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "type": "string" + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": true, + "type": "string" + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "X-Expires-After": { + "type": "string", + "format": "date-time", + "description": "date in UTC when token expires" + }, + "X-Rate-Limit": { + "type": "integer", + "format": "int32", + "description": "calls per hour allowed by the user" + } + }, + "schema": { + "type": "string" + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json", + "application/xml" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "Created user object", + "required": true, + "schema": { + "$ref": "#/definitions/User" + } + } + ], + "responses": { + "default": { + "description": "successful operation" + } + } + } + } + }, + "securityDefinitions": { + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + }, + "petstore_auth": { + "type": "oauth2", + "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", + "flow": "implicit", + "scopes": { + "read:pets": "read your pets", + "write:pets": "modify pets in your account" + } + } + }, + "definitions": { + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Category" + } + }, + "Pet": { + "type": "object", + "required": [ + "name", + "photoUrls" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "category": { + "$ref": "#/definitions/Category" + }, + "name": { + "type": "string", + "example": "doggie" + }, + "photoUrls": { + "type": "array", + "xml": { + "wrapped": true + }, + "items": { + "type": "string", + "xml": { + "name": "photoUrl" + } + } + }, + "tags": { + "type": "array", + "xml": { + "wrapped": true + }, + "items": { + "xml": { + "name": "tag" + }, + "$ref": "#/definitions/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "Pet" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "Tag" + } + }, + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "petId": { + "type": "integer", + "format": "int64" + }, + "quantity": { + "type": "integer", + "format": "int32" + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "Order" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "username": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "userStatus": { + "type": "integer", + "format": "int32", + "description": "User Status" + } + }, + "xml": { + "name": "User" + } + } + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + } +} \ No newline at end of file diff --git a/test/lib/import/input/ref.json b/test/lib/import/input/ref.json new file mode 100644 index 0000000..9d452cc --- /dev/null +++ b/test/lib/import/input/ref.json @@ -0,0 +1,1436 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Service for namespace com.sap.test.MyService", + "description": "This service is located at [/rest/my/](/rest/my/)", + "version": "" + }, + "x-sap-api-type": "ODATAV4", + "x-odata-version": "4.01", + "x-sap-shortText": "Service for namespace com.sap.test.MyService", + "servers": [ + { + "url": "/rest/my" + } + ], + "tags": [ + { + "name": "BaseEntityMyName" + }, + { + "name": "BaseEntityTheirName" + }, + { + "name": "BaseEntityYourName" + } + ], + "paths": { + "/$batch": { + "post": { + "summary": "Sends a group of requests", + "description": "Group multiple requests into a single request payload, see [Batch Requests](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_BatchRequests).\n\n*Please note that \"Try it out\" is not supported for this request.*", + "tags": [ + "Batch Requests" + ], + "requestBody": { + "required": true, + "description": "Batch request", + "content": { + "multipart/mixed;boundary=request-separator": { + "schema": { + "type": "string" + }, + "example": "--request-separator\nContent-Type: application/http\nContent-Transfer-Encoding: binary\n\nGET BaseEntityMyName HTTP/1.1\nAccept: application/json\n\n\n--request-separator--" + } + } + }, + "responses": { + "200": { + "description": "Batch response", + "content": { + "multipart/mixed": { + "schema": { + "type": "string" + }, + "example": "--response-separator\nContent-Type: application/http\n\nHTTP/1.1 200 OK\nContent-Type: application/json\n\n{...}\n--response-separator--" + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/BaseEntityMyName": { + "get": { + "summary": "Retrieves a list of base entity my name.", + "tags": [ + "BaseEntityMyName" + ], + "parameters": [ + { + "$ref": "#/components/parameters/top" + }, + { + "$ref": "#/components/parameters/skip" + }, + { + "$ref": "#/components/parameters/search" + }, + { + "name": "$filter", + "description": "Filter items by property values, see [Filtering](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionfilter)", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/count" + }, + { + "name": "$orderby", + "description": "Order items by property values, see [Sorting](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionorderby)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "id desc", + "refComponent_id", + "refComponent_id desc" + ] + } + } + }, + { + "name": "$select", + "description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "refComponent_id" + ] + } + } + }, + { + "name": "$expand", + "description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "*", + "refComponent" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Retrieved base entity my name", + "content": { + "application/json": { + "schema": { + "type": "object", + "title": "Collection of BaseEntityMyName", + "properties": { + "@count": { + "$ref": "#/components/schemas/count" + }, + "value": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityMyName" + } + } + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + }, + "post": { + "summary": "Creates a single base entity my name.", + "tags": [ + "BaseEntityMyName" + ], + "requestBody": { + "description": "New base entity my name", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityMyName-create" + } + } + } + }, + "responses": { + "201": { + "description": "Created base entity my name", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityMyName" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/BaseEntityMyName({id})": { + "parameters": [ + { + "description": "key: id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + } + } + ], + "get": { + "summary": "Retrieves a single base entity my name.", + "tags": [ + "BaseEntityMyName" + ], + "parameters": [ + { + "name": "$select", + "description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "refComponent_id" + ] + } + } + }, + { + "name": "$expand", + "description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "*", + "refComponent" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Retrieved base entity my name", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityMyName" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + }, + "patch": { + "summary": "Changes a single base entity my name.", + "tags": [ + "BaseEntityMyName" + ], + "requestBody": { + "description": "New property values", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityMyName-update" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + }, + "delete": { + "summary": "Deletes a single base entity my name.", + "tags": [ + "BaseEntityMyName" + ], + "responses": { + "204": { + "description": "Success" + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/BaseEntityMyName({id})/refComponent": { + "parameters": [ + { + "description": "key: id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + } + } + ], + "get": { + "summary": "Retrieves ref component of a base entity my name.", + "tags": [ + "BaseEntityMyName", + "BaseEntityYourName" + ], + "parameters": [ + { + "name": "$select", + "description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "refComponent1_id", + "refComponent2_id" + ] + } + } + }, + { + "name": "$expand", + "description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "*", + "refComponent1", + "refComponent2" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Retrieved ref component", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityYourName" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/BaseEntityTheirName": { + "get": { + "summary": "Retrieves a list of base entity their name.", + "tags": [ + "BaseEntityTheirName" + ], + "parameters": [ + { + "$ref": "#/components/parameters/top" + }, + { + "$ref": "#/components/parameters/skip" + }, + { + "$ref": "#/components/parameters/search" + }, + { + "name": "$filter", + "description": "Filter items by property values, see [Filtering](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionfilter)", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/count" + }, + { + "name": "$orderby", + "description": "Order items by property values, see [Sorting](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionorderby)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "id desc", + "refComponent1_id", + "refComponent1_id desc" + ] + } + } + }, + { + "name": "$select", + "description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "refComponent1_id" + ] + } + } + }, + { + "name": "$expand", + "description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "*", + "refComponent1" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Retrieved base entity their name", + "content": { + "application/json": { + "schema": { + "type": "object", + "title": "Collection of BaseEntityTheirName", + "properties": { + "@count": { + "$ref": "#/components/schemas/count" + }, + "value": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityTheirName" + } + } + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + }, + "post": { + "summary": "Creates a single base entity their name.", + "tags": [ + "BaseEntityTheirName" + ], + "requestBody": { + "description": "New base entity their name", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityTheirName-create" + } + } + } + }, + "responses": { + "201": { + "description": "Created base entity their name", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityTheirName" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/BaseEntityTheirName({id})": { + "parameters": [ + { + "description": "key: id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + } + } + ], + "get": { + "summary": "Retrieves a single base entity their name.", + "tags": [ + "BaseEntityTheirName" + ], + "parameters": [ + { + "name": "$select", + "description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "refComponent1_id" + ] + } + } + }, + { + "name": "$expand", + "description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "*", + "refComponent1" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Retrieved base entity their name", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityTheirName" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + }, + "patch": { + "summary": "Changes a single base entity their name.", + "tags": [ + "BaseEntityTheirName" + ], + "requestBody": { + "description": "New property values", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityTheirName-update" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + }, + "delete": { + "summary": "Deletes a single base entity their name.", + "tags": [ + "BaseEntityTheirName" + ], + "responses": { + "204": { + "description": "Success" + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/BaseEntityTheirName({id})/refComponent1": { + "parameters": [ + { + "description": "key: id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + } + } + ], + "get": { + "summary": "Retrieves ref component1 of a base entity their name.", + "tags": [ + "BaseEntityTheirName", + "BaseEntityMyName" + ], + "parameters": [ + { + "name": "$select", + "description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "refComponent_id" + ] + } + } + }, + { + "name": "$expand", + "description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "*", + "refComponent" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Retrieved ref component1", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityMyName" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/BaseEntityYourName": { + "get": { + "summary": "Retrieves a list of base entity your name.", + "tags": [ + "BaseEntityYourName" + ], + "parameters": [ + { + "$ref": "#/components/parameters/top" + }, + { + "$ref": "#/components/parameters/skip" + }, + { + "$ref": "#/components/parameters/search" + }, + { + "name": "$filter", + "description": "Filter items by property values, see [Filtering](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionfilter)", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "$ref": "#/components/parameters/count" + }, + { + "name": "$orderby", + "description": "Order items by property values, see [Sorting](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionorderby)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "id desc", + "refComponent1_id", + "refComponent1_id desc", + "refComponent2_id", + "refComponent2_id desc" + ] + } + } + }, + { + "name": "$select", + "description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "refComponent1_id", + "refComponent2_id" + ] + } + } + }, + { + "name": "$expand", + "description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "*", + "refComponent1", + "refComponent2" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Retrieved base entity your name", + "content": { + "application/json": { + "schema": { + "type": "object", + "title": "Collection of BaseEntityYourName", + "properties": { + "@count": { + "$ref": "#/components/schemas/count" + }, + "value": { + "type": "array", + "items": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityYourName" + } + } + } + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + }, + "post": { + "summary": "Creates a single base entity your name.", + "tags": [ + "BaseEntityYourName" + ], + "requestBody": { + "description": "New base entity your name", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityYourName-create" + } + } + } + }, + "responses": { + "201": { + "description": "Created base entity your name", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityYourName" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/BaseEntityYourName({id})": { + "parameters": [ + { + "description": "key: id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + } + } + ], + "get": { + "summary": "Retrieves a single base entity your name.", + "tags": [ + "BaseEntityYourName" + ], + "parameters": [ + { + "name": "$select", + "description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "refComponent1_id", + "refComponent2_id" + ] + } + } + }, + { + "name": "$expand", + "description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "*", + "refComponent1", + "refComponent2" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Retrieved base entity your name", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityYourName" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + }, + "patch": { + "summary": "Changes a single base entity your name.", + "tags": [ + "BaseEntityYourName" + ], + "requestBody": { + "description": "New property values", + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityYourName-update" + } + } + } + }, + "responses": { + "204": { + "description": "Success" + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + }, + "delete": { + "summary": "Deletes a single base entity your name.", + "tags": [ + "BaseEntityYourName" + ], + "responses": { + "204": { + "description": "Success" + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/BaseEntityYourName({id})/refComponent1": { + "parameters": [ + { + "description": "key: id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + } + } + ], + "get": { + "summary": "Retrieves ref component1 of a base entity your name.", + "tags": [ + "BaseEntityYourName", + "BaseEntityTheirName" + ], + "parameters": [ + { + "name": "$select", + "description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "refComponent1_id" + ] + } + } + }, + { + "name": "$expand", + "description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "*", + "refComponent1" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Retrieved ref component1", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityTheirName" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + }, + "/BaseEntityYourName({id})/refComponent2": { + "parameters": [ + { + "description": "key: id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + } + } + ], + "get": { + "summary": "Retrieves ref component2 of a base entity your name.", + "tags": [ + "BaseEntityYourName", + "BaseEntityTheirName" + ], + "parameters": [ + { + "name": "$select", + "description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "id", + "refComponent1_id" + ] + } + } + }, + { + "name": "$expand", + "description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "in": "query", + "explode": false, + "schema": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "*", + "refComponent1" + ] + } + } + } + ], + "responses": { + "200": { + "description": "Retrieved ref component2", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityTheirName" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/error" + } + } + } + } + }, + "components": { + "schemas": { + "com.sap.test.MyService.BaseEntityMyName": { + "title": "BaseEntityMyName", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent": { + "allOf": [ + { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityYourName" + } + ], + "nullable": true + }, + "refComponent_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + } + } + }, + "com.sap.test.MyService.BaseEntityMyName-create": { + "title": "BaseEntityMyName (for create)", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + } + } + }, + "com.sap.test.MyService.BaseEntityMyName-update": { + "title": "BaseEntityMyName (for update)", + "type": "object", + "properties": { + "refComponent_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + } + } + }, + "com.sap.test.MyService.BaseEntityTheirName": { + "title": "BaseEntityTheirName", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent1": { + "allOf": [ + { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityMyName" + } + ], + "nullable": true + }, + "refComponent1_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + } + } + }, + "com.sap.test.MyService.BaseEntityTheirName-create": { + "title": "BaseEntityTheirName (for create)", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent1_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + } + } + }, + "com.sap.test.MyService.BaseEntityTheirName-update": { + "title": "BaseEntityTheirName (for update)", + "type": "object", + "properties": { + "refComponent1_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + } + } + }, + "com.sap.test.MyService.BaseEntityYourName": { + "title": "BaseEntityYourName", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent1": { + "allOf": [ + { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityTheirName" + } + ], + "nullable": true + }, + "refComponent1_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + }, + "refComponent2": { + "allOf": [ + { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityTheirName" + } + ], + "nullable": true + }, + "refComponent2_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + }, + "refComponent3": { + "allOf": [ + { + "$ref": "#/components/schemas/com.sap.test.MyService.BaseEntityYourName" + } + ], + "nullable": true + } + } + }, + "com.sap.test.MyService.BaseEntityYourName-create": { + "title": "BaseEntityYourName (for create)", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent1_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + }, + "refComponent2_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + } + } + }, + "com.sap.test.MyService.BaseEntityYourName-update": { + "title": "BaseEntityYourName (for update)", + "type": "object", + "properties": { + "refComponent1_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + }, + "refComponent2_id": { + "type": "string", + "format": "uuid", + "example": "01234567-89ab-cdef-0123-456789abcdef", + "nullable": true + } + } + }, + "count": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "description": "The number of entities in the collection. Available when using the [$count](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptioncount) query option." + }, + "error": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "target": { + "type": "string" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + }, + "target": { + "type": "string" + } + } + } + }, + "innererror": { + "type": "object", + "description": "The structure of this object is service-specific" + } + } + } + } + } + }, + "parameters": { + "top": { + "name": "$top", + "in": "query", + "description": "Show only the first n items, see [Paging - Top](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptiontop)", + "schema": { + "type": "integer", + "minimum": 0 + }, + "example": 50 + }, + "skip": { + "name": "$skip", + "in": "query", + "description": "Skip the first n items, see [Paging - Skip](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionskip)", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "count": { + "name": "$count", + "in": "query", + "description": "Include count of items, see [Count](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptioncount)", + "schema": { + "type": "boolean" + } + }, + "search": { + "name": "$search", + "in": "query", + "description": "Search items by search phrases, see [Searching](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionsearch)", + "schema": { + "type": "string" + } + } + }, + "responses": { + "error": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/error" + } + } + } + } + } + } + } \ No newline at end of file diff --git a/test/lib/import/input/wizard-world.json b/test/lib/import/input/wizard-world.json new file mode 100644 index 0000000..538aed6 --- /dev/null +++ b/test/lib/import/input/wizard-world.json @@ -0,0 +1,928 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "WizardWorldApi", + "contact": { + "name": "Github", + "url": "https://github.com/MossPiglets/WizardWorldAPI" + }, + "version": "1.0.1" + }, + "servers": [ + { + "url": "https://wizard-world-api.herokuapp.com" + } + ], + "paths": { + "/Elixirs": { + "get": { + "tags": [ + "Elixirs" + ], + "parameters": [ + { + "name": "Name", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "Difficulty", + "in": "query", + "schema": { + "$ref": "#/components/schemas/ElixirDifficulty" + } + }, + { + "name": "Ingredient", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "InventorFullName", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "Manufacturer", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ElixirDto" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ElixirDto" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ElixirDto" + } + } + } + } + } + } + } + }, + "/Elixirs/{id}": { + "get": { + "tags": [ + "Elixirs" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/ElixirDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/ElixirDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/ElixirDto" + } + } + } + } + } + } + }, + "/Feedback": { + "post": { + "tags": [ + "Feedback" + ], + "requestBody": { + "content": { + "application/json-patch+json": { + "schema": { + "$ref": "#/components/schemas/SendFeedbackCommand" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/SendFeedbackCommand" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SendFeedbackCommand" + } + }, + "application/*+json": { + "schema": { + "$ref": "#/components/schemas/SendFeedbackCommand" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/Unit" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Unit" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/Unit" + } + } + } + } + } + } + }, + "/Houses": { + "get": { + "tags": [ + "Houses" + ], + "parameters": [ + { + "name": "query", + "in": "query", + "schema": { + "$ref": "#/components/schemas/GetHousesQuery" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HouseDto" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HouseDto" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HouseDto" + } + } + } + } + } + } + } + }, + "/Houses/{id}": { + "get": { + "tags": [ + "Houses" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/HouseDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/HouseDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/HouseDto" + } + } + } + } + } + } + }, + "/Ingredients": { + "get": { + "tags": [ + "Ingredients" + ], + "parameters": [ + { + "name": "Name", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IngredientDto" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IngredientDto" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IngredientDto" + } + } + } + } + } + } + } + }, + "/Ingredients/{id}": { + "get": { + "tags": [ + "Ingredients" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/IngredientDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/IngredientDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/IngredientDto" + } + } + } + } + } + } + }, + "/Spells": { + "get": { + "tags": [ + "Spells" + ], + "parameters": [ + { + "name": "Name", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "Type", + "in": "query", + "schema": { + "$ref": "#/components/schemas/SpellType" + } + }, + { + "name": "Incantation", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SpellDto" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SpellDto" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SpellDto" + } + } + } + } + } + } + } + }, + "/Spells/{id}": { + "get": { + "tags": [ + "Spells" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/SpellDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/SpellDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/SpellDto" + } + } + } + } + } + } + }, + "/Wizards": { + "get": { + "tags": [ + "Wizards" + ], + "parameters": [ + { + "name": "FirstName", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "LastName", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WizardDto" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WizardDto" + } + } + }, + "text/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WizardDto" + } + } + } + } + } + } + } + }, + "/Wizards/{id}": { + "get": { + "tags": [ + "Wizards" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "text/plain": { + "schema": { + "$ref": "#/components/schemas/WizardDto" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/WizardDto" + } + }, + "text/json": { + "schema": { + "$ref": "#/components/schemas/WizardDto" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "ElixirDifficulty": { + "enum": [ + "Unknown", + "Advanced", + "Moderate", + "Beginner", + "OrdinaryWizardingLevel", + "OneOfAKind" + ], + "type": "string" + }, + "ElixirDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + }, + "effect": { + "type": "string", + "nullable": true + }, + "sideEffects": { + "type": "string", + "nullable": true + }, + "characteristics": { + "type": "string", + "nullable": true + }, + "time": { + "type": "string", + "nullable": true + }, + "difficulty": { + "$ref": "#/components/schemas/ElixirDifficulty" + }, + "ingredients": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IngredientDto" + }, + "nullable": true + }, + "inventors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ElixirInventorDto" + }, + "nullable": true + }, + "manufacturer": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "ElixirInventorDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "firstName": { + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "FeedbackType": { + "enum": [ + "General", + "Bug", + "DataError", + "Suggestion" + ], + "type": "string" + }, + "GetHousesQuery": { + "type": "object", + "additionalProperties": false + }, + "HouseDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + }, + "houseColours": { + "type": "string", + "nullable": true + }, + "founder": { + "type": "string", + "nullable": true + }, + "animal": { + "type": "string", + "nullable": true + }, + "element": { + "type": "string", + "nullable": true + }, + "ghost": { + "type": "string", + "nullable": true + }, + "commonRoom": { + "type": "string", + "nullable": true + }, + "heads": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HouseHeadDto" + }, + "nullable": true + }, + "traits": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TraitDto" + }, + "nullable": true + } + }, + "additionalProperties": false + }, + "HouseHeadDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "firstName": { + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "IngredientDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "SendFeedbackCommand": { + "type": "object", + "properties": { + "feedbackType": { + "$ref": "#/components/schemas/FeedbackType" + }, + "feedback": { + "type": "string", + "nullable": true + }, + "entityId": { + "type": "string", + "format": "uuid", + "nullable": true + } + }, + "additionalProperties": false + }, + "SpellDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + }, + "incantation": { + "type": "string", + "nullable": true + }, + "effect": { + "type": "string", + "nullable": true + }, + "canBeVerbal": { + "type": "boolean", + "nullable": true + }, + "type": { + "$ref": "#/components/schemas/SpellType" + }, + "light": { + "$ref": "#/components/schemas/SpellLight" + }, + "creator": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "SpellLight": { + "enum": [ + "None", + "Blue", + "IcyBlue", + "Red", + "Gold", + "Purple", + "Transparent", + "White", + "Green", + "Orange", + "Yellow", + "BrightBlue", + "Pink", + "Violet", + "BlueishWhite", + "Silver", + "Scarlet", + "Fire", + "FieryScarlet", + "Grey", + "DarkRed", + "Turquoise", + "PsychedelicTransparentWave", + "BrightYellow", + "BlackSmoke" + ], + "type": "string" + }, + "SpellType": { + "enum": [ + "None", + "Charm", + "Conjuration", + "Spell", + "Transfiguration", + "HealingSpell", + "DarkCharm", + "Jinx", + "Curse", + "MagicalTransportation", + "Hex", + "CounterSpell", + "DarkArts", + "CounterJinx", + "CounterCharm", + "Untransfiguration", + "BindingMagicalContract", + "Vanishment" + ], + "type": "string" + }, + "TraitDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "$ref": "#/components/schemas/TraitName" + } + }, + "additionalProperties": false + }, + "TraitName": { + "enum": [ + "None", + "Courage", + "Bravery", + "Determination", + "Daring", + "Nerve", + "Chivalary", + "Hardworking", + "Patience", + "Fairness", + "Just", + "Loyalty", + "Modesty", + "Wit", + "Learning", + "Wisdom", + "Acceptance", + "Inteligence", + "Creativity", + "Resourcefulness", + "Pride", + "Cunning", + "Ambition", + "Selfpreservation" + ], + "type": "string" + }, + "Unit": { + "type": "object", + "additionalProperties": false + }, + "WizardDto": { + "type": "object", + "properties": { + "elixirs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WizardElixirDto" + }, + "nullable": true + }, + "id": { + "type": "string", + "format": "uuid" + }, + "firstName": { + "type": "string", + "nullable": true + }, + "lastName": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + }, + "WizardElixirDto": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "nullable": true + } + }, + "additionalProperties": false + } + } + } +} \ No newline at end of file diff --git a/test/lib/import/output/petstore.csn b/test/lib/import/output/petstore.csn new file mode 100644 index 0000000..8dde442 --- /dev/null +++ b/test/lib/import/output/petstore.csn @@ -0,0 +1,91 @@ +{ + "definitions": { + "Swagger.Petstore": { + "kind": "service", + "@Capabilities.BatchSupported": false, + "@Capabilities.KeyAsSegmentSupported": true, + "@Core.Description": "Swagger Petstore", + "@Core.SchemaVersion": "1.0.0" + }, + "Swagger.Petstore.pets": { + "kind": "function", + "params": { + "limit": { + "type": "cds.Integer", + "@description": "How many items to return at one time (max 100)", + "@openapi.in": "query", + "default": { + "val": "213" + } + } + }, + "@Common.Label": "pets", + "@Core.Description": "List all pets", + "@openapi.path": "/pets", + "returns": { + "type": "Swagger.Petstore_types.Pets" + } + }, + "Swagger.Petstore.pets_post": { + "kind": "action", + "params": {}, + "@Common.Label": "pets", + "@Core.Description": "Create a pet", + "@openapi.path": "/pets" + }, + "Swagger.Petstore.pets_": { + "kind": "function", + "params": { + "petId": { + "type": "cds.String", + "@description": "The id of the pet to retrieve", + "@openapi.in": "path" + } + }, + "@Common.Label": "pets", + "@Core.Description": "Info for a specific pet", + "@openapi.path": "/pets/{petId}", + "returns": { + "type": "Swagger.Petstore_types.Pet" + } + }, + "Swagger.Petstore_types.Pet": { + "elements": { + "id": { + "type": "cds.Integer64", + "@mandatory": true + }, + "name": { + "type": "cds.String", + "@mandatory": true + }, + "tag": { + "type": "cds.String" + } + }, + "kind": "type" + }, + "Swagger.Petstore_types.Pets": { + "items": { + "type": "Swagger.Petstore_types.Pet" + }, + "kind": "type" + }, + "Swagger.Petstore_types.Error": { + "elements": { + "code": { + "type": "cds.Integer", + "@mandatory": true + }, + "message": { + "type": "cds.String", + "@mandatory": true + } + }, + "kind": "type" + } + }, + "meta": { + "creator": "cds-import-openapi" + } + } \ No newline at end of file diff --git a/test/lib/import/output/petstore.swagger.csn b/test/lib/import/output/petstore.swagger.csn new file mode 100644 index 0000000..d891e51 --- /dev/null +++ b/test/lib/import/output/petstore.swagger.csn @@ -0,0 +1,481 @@ +{ + "definitions": { + "Swagger.Petstore": { + "kind": "service", + "@Capabilities.BatchSupported": false, + "@Capabilities.KeyAsSegmentSupported": true, + "@Core.Description": "Swagger Petstore", + "@Core.SchemaVersion": "1.0.6", + "@Core.LongDescription": "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters." + }, + "Swagger.Petstore.pet__uploadImage_post": { + "kind": "action", + "params": { + "petId": { + "@description": "ID of pet to update", + "type": "cds.Integer64", + "@openapi.in": "path" + }, + "additionalMetadata": { + "@description": "Additional data to pass to server", + "type": "cds.String", + "default": { + "val": "32" + }, + "@openapi.in": "formData" + }, + "file": { + "@description": "file to upload", + "type": "cds.String", + "@openapi.in": "formData", + "default": { + "val": "Empty" + } + } + }, + "@Common.Label": "pet", + "@Core.Description": "uploads an image", + "@openapi.path": "/pet/{petId}/uploadImage", + "returns": { + "type": "Swagger.Petstore_types.ApiResponse" + } + }, + "Swagger.Petstore.pet_post": { + "kind": "action", + "params": { + "body": { + "type": "Swagger.Petstore_types.Pet", + "@openapi.in": "body" + } + }, + "@Common.Label": "pet", + "@Core.Description": "Add a new pet to the store", + "@openapi.path": "/pet" + }, + "Swagger.Petstore.pet_put": { + "kind": "action", + "params": { + "body": { + "type": "Swagger.Petstore_types.Pet", + "@openapi.in": "body" + } + }, + "@Common.Label": "pet", + "@Core.Description": "Update an existing pet", + "@openapi.method": "PUT", + "@openapi.path": "/pet" + }, + "Swagger.Petstore.pet_findByStatus": { + "kind": "function", + "params": { + "status": { + "@description": "Status values that need to be considered for filter", + "items": { + "type": "Swagger.Petstore.anonymous.type0" + }, + "@openapi.in": "query", + "@openapi.required": true + } + }, + "@Common.Label": "pet", + "@Core.Description": "Finds Pets by status", + "@Core.LongDescription": "Multiple status values can be provided with comma separated strings", + "@openapi.path": "/pet/findByStatus", + "returns": { + "items": { + "type": "Swagger.Petstore_types.Pet" + } + } + }, + "Swagger.Petstore.pet_": { + "kind": "function", + "params": { + "petId": { + "@description": "ID of pet to return", + "type": "cds.Integer64", + "@openapi.in": "path" + } + }, + "@Common.Label": "pet", + "@Core.Description": "Find pet by ID", + "@Core.LongDescription": "Returns a single pet", + "@openapi.path": "/pet/{petId}", + "returns": { + "type": "Swagger.Petstore_types.Pet" + } + }, + "Swagger.Petstore.pet__post": { + "kind": "action", + "params": { + "petId": { + "@description": "ID of pet that needs to be updated", + "type": "cds.Integer64", + "@openapi.in": "path" + }, + "name": { + "@description": "Updated name of the pet", + "type": "cds.String", + "@openapi.in": "formData" + }, + "status": { + "@description": "Updated status of the pet", + "type": "cds.String", + "@openapi.in": "formData" + } + }, + "@Common.Label": "pet", + "@Core.Description": "Updates a pet in the store with form data", + "@openapi.path": "/pet/{petId}" + }, + "Swagger.Petstore.pet__delete": { + "kind": "action", + "params": { + "api_key": { + "type": "cds.String", + "@openapi.in": "header" + }, + "petId": { + "@description": "Pet id to delete", + "type": "cds.Integer64", + "@openapi.in": "path" + } + }, + "@Common.Label": "pet", + "@Core.Description": "Deletes a pet", + "@openapi.method": "DELETE", + "@openapi.path": "/pet/{petId}" + }, + "Swagger.Petstore.store_order_post": { + "kind": "action", + "params": { + "body": { + "type": "Swagger.Petstore_types.Order", + "@openapi.in": "body" + } + }, + "@Common.Label": "store", + "@Core.Description": "Place an order for a pet", + "@openapi.path": "/store/order", + "returns": { + "type": "Swagger.Petstore_types.Order" + } + }, + "Swagger.Petstore.store_order_": { + "kind": "function", + "params": { + "orderId": { + "@description": "ID of pet that needs to be fetched", + "type": "cds.Integer64", + "@openapi.in": "path" + } + }, + "@Common.Label": "store", + "@Core.Description": "Find purchase order by ID", + "@Core.LongDescription": "For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions", + "@openapi.path": "/store/order/{orderId}", + "returns": { + "type": "Swagger.Petstore_types.Order" + } + }, + "Swagger.Petstore.store_order__delete": { + "kind": "action", + "params": { + "orderId": { + "@description": "ID of the order that needs to be deleted", + "type": "cds.Integer64", + "@openapi.in": "path" + } + }, + "@Common.Label": "store", + "@Core.Description": "Delete purchase order by ID", + "@Core.LongDescription": "For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors", + "@openapi.method": "DELETE", + "@openapi.path": "/store/order/{orderId}" + }, + "Swagger.Petstore.store_inventory": { + "kind": "function", + "params": {}, + "@Common.Label": "store", + "@Core.Description": "Returns pet inventories by status", + "@Core.LongDescription": "Returns a map of status codes to quantities", + "@openapi.path": "/store/inventory", + "returns": { + "elements": {} + } + }, + "Swagger.Petstore.user_createWithArray_post": { + "kind": "action", + "params": { + "body": { + "items": { + "type": "Swagger.Petstore_types.User" + }, + "@openapi.in": "body" + } + }, + "@Common.Label": "user", + "@Core.Description": "Creates list of users with given input array", + "@openapi.path": "/user/createWithArray" + }, + "Swagger.Petstore.user_createWithList_post": { + "kind": "action", + "params": { + "body": { + "items": { + "type": "Swagger.Petstore_types.User" + }, + "@openapi.in": "body" + } + }, + "@Common.Label": "user", + "@Core.Description": "Creates list of users with given input array", + "@openapi.path": "/user/createWithList" + }, + "Swagger.Petstore.user_": { + "kind": "function", + "params": { + "username": { + "@description": "The name that needs to be fetched. Use user1 for testing. ", + "type": "cds.String", + "@openapi.in": "path" + } + }, + "@Common.Label": "user", + "@Core.Description": "Get user by user name", + "@openapi.path": "/user/{username}", + "returns": { + "type": "Swagger.Petstore_types.User" + } + }, + "Swagger.Petstore.user__put": { + "kind": "action", + "params": { + "username": { + "@description": "name that need to be updated", + "type": "cds.String", + "@openapi.in": "path" + }, + "body": { + "type": "Swagger.Petstore_types.User", + "@openapi.in": "body" + } + }, + "@Common.Label": "user", + "@Core.Description": "Updated user", + "@Core.LongDescription": "This can only be done by the logged in user.", + "@openapi.method": "PUT", + "@openapi.path": "/user/{username}" + }, + "Swagger.Petstore.user__delete": { + "kind": "action", + "params": { + "username": { + "@description": "The name that needs to be deleted", + "type": "cds.String", + "@openapi.in": "path" + } + }, + "@Common.Label": "user", + "@Core.Description": "Delete user", + "@Core.LongDescription": "This can only be done by the logged in user.", + "@openapi.method": "DELETE", + "@openapi.path": "/user/{username}" + }, + "Swagger.Petstore.user_login": { + "kind": "function", + "params": { + "username": { + "@description": "The user name for login", + "type": "cds.String", + "@openapi.in": "query", + "@openapi.required": true + }, + "password": { + "@description": "The password for login in clear text", + "type": "cds.String", + "@openapi.in": "query", + "@openapi.required": true + } + }, + "@Common.Label": "user", + "@Core.Description": "Logs user into the system", + "@openapi.path": "/user/login", + "returns": { + "type": "cds.String" + } + }, + "Swagger.Petstore.user_logout": { + "kind": "function", + "params": {}, + "@Common.Label": "user", + "@Core.Description": "Logs out current logged in user session", + "@openapi.path": "/user/logout", + "returns": { + "type": "cds.Boolean" + } + }, + "Swagger.Petstore.user_post": { + "kind": "action", + "params": { + "body": { + "type": "Swagger.Petstore_types.User", + "@openapi.in": "body" + } + }, + "@Common.Label": "user", + "@Core.Description": "Create user", + "@Core.LongDescription": "This can only be done by the logged in user.", + "@openapi.path": "/user" + }, + "Swagger.Petstore_types.ApiResponse": { + "elements": { + "code": { + "type": "cds.Integer" + }, + "type": { + "type": "cds.String" + }, + "message": { + "type": "cds.String" + } + }, + "kind": "type" + }, + "Swagger.Petstore_types.Category": { + "elements": { + "id": { + "type": "cds.Integer64" + }, + "name": { + "type": "cds.String" + } + }, + "kind": "type" + }, + "Swagger.Petstore_types.Pet": { + "elements": { + "id": { + "type": "cds.Integer64" + }, + "category": { + "type": "Swagger.Petstore_types.Category" + }, + "name": { + "type": "cds.String", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "doggie", + "@mandatory": true + }, + "photoUrls": { + "items": { + "type": "cds.String" + }, + "@mandatory": true + }, + "tags": { + "items": { + "type": "Swagger.Petstore_types.Tag" + } + }, + "status": { + "@description": "pet status in the store", + "type": "cds.String", + "@assert.range": true, + "enum": { + "available": {}, + "pending": {}, + "sold": {} + } + } + }, + "kind": "type" + }, + "Swagger.Petstore_types.Tag": { + "elements": { + "id": { + "type": "cds.Integer64" + }, + "name": { + "type": "cds.String" + } + }, + "kind": "type" + }, + "Swagger.Petstore_types.Order": { + "elements": { + "id": { + "type": "cds.Integer64" + }, + "petId": { + "type": "cds.Integer64" + }, + "quantity": { + "type": "cds.Integer" + }, + "shipDate": { + "type": "cds.Timestamp" + }, + "status": { + "@description": "Order Status", + "type": "cds.String", + "@assert.range": true, + "enum": { + "placed": {}, + "approved": {}, + "delivered": {} + } + }, + "complete": { + "type": "cds.Boolean" + } + }, + "kind": "type" + }, + "Swagger.Petstore_types.User": { + "elements": { + "id": { + "type": "cds.Integer64" + }, + "username": { + "type": "cds.String" + }, + "firstName": { + "type": "cds.String" + }, + "lastName": { + "type": "cds.String" + }, + "email": { + "type": "cds.String" + }, + "password": { + "type": "cds.String" + }, + "phone": { + "type": "cds.String" + }, + "userStatus": { + "@description": "User Status", + "type": "cds.Integer" + } + }, + "kind": "type" + }, + "Swagger.Petstore.anonymous.type0": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "available": {}, + "pending": {}, + "sold": {} + }, + "default": { + "val": "available" + }, + "kind": "type" + } + }, + "meta": { + "creator": "cds-import-openapi" + } + } \ No newline at end of file diff --git a/test/lib/import/output/ref.csn b/test/lib/import/output/ref.csn new file mode 100644 index 0000000..ce9cb90 --- /dev/null +++ b/test/lib/import/output/ref.csn @@ -0,0 +1,1112 @@ +{ + "definitions": { + "Service.for.namespace.com.sap.test.MyService": { + "kind": "service", + "@Capabilities.BatchSupported": false, + "@Capabilities.KeyAsSegmentSupported": true, + "@Core.Description": "Service for namespace com.sap.test.MyService", + "@Core.LongDescription": "This service is located at [/rest/my/](/rest/my/)" + }, + "Service.for.namespace.com.sap.test.MyService._batch_post": { + "kind": "action", + "params": { + "body": { + "@openapi.contentType": "multipart/mixed;boundary=request-separator", + "type": "cds.String", + "@openapi.in": "body" + } + }, + "@Common.Label": "Batch Requests", + "@Core.Description": "Sends a group of requests", + "@Core.LongDescription": "Group multiple requests into a single request payload, see [Batch Requests](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_BatchRequests).\n\n*Please note that \"Try it out\" is not supported for this request.*", + "@openapi.path": "/$batch", + "returns": { + "@openapi.contentType": "multipart/mixed", + "type": "cds.String" + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityMyName": { + "kind": "function", + "params": { + "_top": { + "type": "cds.Integer", + "@description": "Show only the first n items, see [Paging - Top](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptiontop)", + "@openapi.in": "query", + "@openapi.name": "$top" + }, + "_skip": { + "type": "cds.Integer", + "@description": "Skip the first n items, see [Paging - Skip](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionskip)", + "@openapi.in": "query", + "@openapi.name": "$skip" + }, + "_search": { + "type": "cds.String", + "@description": "Search items by search phrases, see [Searching](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionsearch)", + "@openapi.in": "query", + "@openapi.name": "$search" + }, + "_filter": { + "type": "cds.String", + "@description": "Filter items by property values, see [Filtering](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionfilter)", + "@openapi.in": "query", + "@openapi.name": "$filter" + }, + "_count": { + "type": "cds.Boolean", + "@description": "Include count of items, see [Count](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptioncount)", + "@openapi.in": "query", + "@openapi.name": "$count" + }, + "_orderby": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type0" + }, + "@description": "Order items by property values, see [Sorting](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionorderby)", + "@openapi.in": "query", + "@openapi.name": "$orderby" + }, + "_select": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type1" + }, + "@description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "@openapi.in": "query", + "@openapi.name": "$select" + }, + "_expand": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type2" + }, + "@description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "@openapi.in": "query", + "@openapi.name": "$expand" + } + }, + "@Common.Label": "BaseEntityMyName", + "@Core.Description": "Retrieves a list of base entity my name.", + "@openapi.path": "/BaseEntityMyName", + "returns": { + "@title": "Collection of BaseEntityMyName", + "elements": { + "_count": { + "type": "Service.for.namespace.com.sap.test.MyService_types.count", + "@openapi.name": "@count" + }, + "value": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityMyName" + } + } + } + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityMyName_post": { + "kind": "action", + "params": { + "body": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityMyName_create", + "@openapi.in": "body" + } + }, + "@Common.Label": "BaseEntityMyName", + "@Core.Description": "Creates a single base entity my name.", + "@openapi.path": "/BaseEntityMyName", + "returns": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityMyName" + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityMyName___": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + }, + "_select": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type3" + }, + "@description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "@openapi.in": "query", + "@openapi.name": "$select" + }, + "_expand": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type4" + }, + "@description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "@openapi.in": "query", + "@openapi.name": "$expand" + } + }, + "@Common.Label": "BaseEntityMyName", + "@Core.Description": "Retrieves a single base entity my name.", + "@openapi.path": "/BaseEntityMyName({id})", + "returns": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityMyName" + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityMyName____patch": { + "kind": "action", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + }, + "body": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityMyName_update", + "@openapi.in": "body" + } + }, + "@Common.Label": "BaseEntityMyName", + "@Core.Description": "Changes a single base entity my name.", + "@openapi.method": "PATCH", + "@openapi.path": "/BaseEntityMyName({id})" + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityMyName____delete": { + "kind": "action", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + } + }, + "@Common.Label": "BaseEntityMyName", + "@Core.Description": "Deletes a single base entity my name.", + "@openapi.method": "DELETE", + "@openapi.path": "/BaseEntityMyName({id})" + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityMyName____refComponent": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + }, + "_select": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type5" + }, + "@description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "@openapi.in": "query", + "@openapi.name": "$select" + }, + "_expand": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type6" + }, + "@description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "@openapi.in": "query", + "@openapi.name": "$expand" + } + }, + "@Common.Label": "BaseEntityMyName", + "@Core.Description": "Retrieves ref component of a base entity my name.", + "@openapi.path": "/BaseEntityMyName({id})/refComponent", + "returns": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName" + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityTheirName": { + "kind": "function", + "params": { + "_top": { + "type": "cds.Integer", + "@description": "Show only the first n items, see [Paging - Top](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptiontop)", + "@openapi.in": "query", + "@openapi.name": "$top" + }, + "_skip": { + "type": "cds.Integer", + "@description": "Skip the first n items, see [Paging - Skip](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionskip)", + "@openapi.in": "query", + "@openapi.name": "$skip" + }, + "_search": { + "type": "cds.String", + "@description": "Search items by search phrases, see [Searching](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionsearch)", + "@openapi.in": "query", + "@openapi.name": "$search" + }, + "_filter": { + "type": "cds.String", + "@description": "Filter items by property values, see [Filtering](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionfilter)", + "@openapi.in": "query", + "@openapi.name": "$filter" + }, + "_count": { + "type": "cds.Boolean", + "@description": "Include count of items, see [Count](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptioncount)", + "@openapi.in": "query", + "@openapi.name": "$count" + }, + "_orderby": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type7" + }, + "@description": "Order items by property values, see [Sorting](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionorderby)", + "@openapi.in": "query", + "@openapi.name": "$orderby" + }, + "_select": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type8" + }, + "@description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "@openapi.in": "query", + "@openapi.name": "$select" + }, + "_expand": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type9" + }, + "@description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "@openapi.in": "query", + "@openapi.name": "$expand" + } + }, + "@Common.Label": "BaseEntityTheirName", + "@Core.Description": "Retrieves a list of base entity their name.", + "@openapi.path": "/BaseEntityTheirName", + "returns": { + "@title": "Collection of BaseEntityTheirName", + "elements": { + "_count": { + "type": "Service.for.namespace.com.sap.test.MyService_types.count", + "@openapi.name": "@count" + }, + "value": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName" + } + } + } + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityTheirName_post": { + "kind": "action", + "params": { + "body": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName_create", + "@openapi.in": "body" + } + }, + "@Common.Label": "BaseEntityTheirName", + "@Core.Description": "Creates a single base entity their name.", + "@openapi.path": "/BaseEntityTheirName", + "returns": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName" + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityTheirName___": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + }, + "_select": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type10" + }, + "@description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "@openapi.in": "query", + "@openapi.name": "$select" + }, + "_expand": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type11" + }, + "@description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "@openapi.in": "query", + "@openapi.name": "$expand" + } + }, + "@Common.Label": "BaseEntityTheirName", + "@Core.Description": "Retrieves a single base entity their name.", + "@openapi.path": "/BaseEntityTheirName({id})", + "returns": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName" + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityTheirName____patch": { + "kind": "action", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + }, + "body": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName_update", + "@openapi.in": "body" + } + }, + "@Common.Label": "BaseEntityTheirName", + "@Core.Description": "Changes a single base entity their name.", + "@openapi.method": "PATCH", + "@openapi.path": "/BaseEntityTheirName({id})" + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityTheirName____delete": { + "kind": "action", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + } + }, + "@Common.Label": "BaseEntityTheirName", + "@Core.Description": "Deletes a single base entity their name.", + "@openapi.method": "DELETE", + "@openapi.path": "/BaseEntityTheirName({id})" + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityTheirName____refComponent1": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + }, + "_select": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type12" + }, + "@description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "@openapi.in": "query", + "@openapi.name": "$select" + }, + "_expand": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type13" + }, + "@description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "@openapi.in": "query", + "@openapi.name": "$expand" + } + }, + "@Common.Label": "BaseEntityTheirName", + "@Core.Description": "Retrieves ref component1 of a base entity their name.", + "@openapi.path": "/BaseEntityTheirName({id})/refComponent1", + "returns": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityMyName" + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityYourName": { + "kind": "function", + "params": { + "_top": { + "type": "cds.Integer", + "@description": "Show only the first n items, see [Paging - Top](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptiontop)", + "@openapi.in": "query", + "@openapi.name": "$top" + }, + "_skip": { + "type": "cds.Integer", + "@description": "Skip the first n items, see [Paging - Skip](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionskip)", + "@openapi.in": "query", + "@openapi.name": "$skip" + }, + "_search": { + "type": "cds.String", + "@description": "Search items by search phrases, see [Searching](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionsearch)", + "@openapi.in": "query", + "@openapi.name": "$search" + }, + "_filter": { + "type": "cds.String", + "@description": "Filter items by property values, see [Filtering](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionfilter)", + "@openapi.in": "query", + "@openapi.name": "$filter" + }, + "_count": { + "type": "cds.Boolean", + "@description": "Include count of items, see [Count](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptioncount)", + "@openapi.in": "query", + "@openapi.name": "$count" + }, + "_orderby": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type14" + }, + "@description": "Order items by property values, see [Sorting](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionorderby)", + "@openapi.in": "query", + "@openapi.name": "$orderby" + }, + "_select": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type15" + }, + "@description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "@openapi.in": "query", + "@openapi.name": "$select" + }, + "_expand": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type16" + }, + "@description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "@openapi.in": "query", + "@openapi.name": "$expand" + } + }, + "@Common.Label": "BaseEntityYourName", + "@Core.Description": "Retrieves a list of base entity your name.", + "@openapi.path": "/BaseEntityYourName", + "returns": { + "@title": "Collection of BaseEntityYourName", + "elements": { + "_count": { + "type": "Service.for.namespace.com.sap.test.MyService_types.count", + "@openapi.name": "@count" + }, + "value": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName" + } + } + } + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityYourName_post": { + "kind": "action", + "params": { + "body": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName_create", + "@openapi.in": "body" + } + }, + "@Common.Label": "BaseEntityYourName", + "@Core.Description": "Creates a single base entity your name.", + "@openapi.path": "/BaseEntityYourName", + "returns": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName" + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityYourName___": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + }, + "_select": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type17" + }, + "@description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "@openapi.in": "query", + "@openapi.name": "$select" + }, + "_expand": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type18" + }, + "@description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "@openapi.in": "query", + "@openapi.name": "$expand" + } + }, + "@Common.Label": "BaseEntityYourName", + "@Core.Description": "Retrieves a single base entity your name.", + "@openapi.path": "/BaseEntityYourName({id})", + "returns": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName" + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityYourName____patch": { + "kind": "action", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + }, + "body": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName_update", + "@openapi.in": "body" + } + }, + "@Common.Label": "BaseEntityYourName", + "@Core.Description": "Changes a single base entity your name.", + "@openapi.method": "PATCH", + "@openapi.path": "/BaseEntityYourName({id})" + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityYourName____delete": { + "kind": "action", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + } + }, + "@Common.Label": "BaseEntityYourName", + "@Core.Description": "Deletes a single base entity your name.", + "@openapi.method": "DELETE", + "@openapi.path": "/BaseEntityYourName({id})" + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityYourName____refComponent1": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + }, + "_select": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type19" + }, + "@description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "@openapi.in": "query", + "@openapi.name": "$select" + }, + "_expand": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type20" + }, + "@description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "@openapi.in": "query", + "@openapi.name": "$expand" + } + }, + "@Common.Label": "BaseEntityYourName", + "@Core.Description": "Retrieves ref component1 of a base entity your name.", + "@openapi.path": "/BaseEntityYourName({id})/refComponent1", + "returns": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName" + } + }, + "Service.for.namespace.com.sap.test.MyService.BaseEntityYourName____refComponent2": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef", + "@description": "key: id", + "@openapi.in": "path" + }, + "_select": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type21" + }, + "@description": "Select properties to be returned, see [Select](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionselect)", + "@openapi.in": "query", + "@openapi.name": "$select" + }, + "_expand": { + "items": { + "type": "Service.for.namespace.com.sap.test.MyService.anonymous.type22" + }, + "@description": "The value of $expand query option is a comma-separated list of navigation property names, stream property names, or $value indicating the stream content of a media-entity. The corresponding related entities and stream values will be represented inline, see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptionexpand)", + "@openapi.in": "query", + "@openapi.name": "$expand" + } + }, + "@Common.Label": "BaseEntityYourName", + "@Core.Description": "Retrieves ref component2 of a base entity your name.", + "@openapi.path": "/BaseEntityYourName({id})/refComponent2", + "returns": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName" + } + }, + "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityMyName": { + "@title": "BaseEntityMyName", + "elements": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName" + }, + "refComponent_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityMyName_create": { + "@title": "BaseEntityMyName (for create)", + "elements": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityMyName_update": { + "@title": "BaseEntityMyName (for update)", + "elements": { + "refComponent_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName": { + "@title": "BaseEntityTheirName", + "elements": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent1": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityMyName" + }, + "refComponent1_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName_create": { + "@title": "BaseEntityTheirName (for create)", + "elements": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent1_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName_update": { + "@title": "BaseEntityTheirName (for update)", + "elements": { + "refComponent1_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName": { + "@title": "BaseEntityYourName", + "elements": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent1": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName" + }, + "refComponent1_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent2": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityTheirName" + }, + "refComponent2_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent3": { + "type": "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName_create": { + "@title": "BaseEntityYourName (for create)", + "elements": { + "id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent1_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent2_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService_types.com_sap_test_MyService_BaseEntityYourName_update": { + "@title": "BaseEntityYourName (for update)", + "elements": { + "refComponent1_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + }, + "refComponent2_id": { + "type": "cds.UUID", + "@Core.Example.$Type": "Core.PrimitiveExampleValue", + "@Core.Example.Value": "01234567-89ab-cdef-0123-456789abcdef" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService_types.count": { + "@description": "The number of entities in the collection. Available when using the [$count](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_SystemQueryOptioncount) query option.", + "type": "cds.String", + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService_types.error": { + "elements": { + "error": { + "elements": { + "code": { + "type": "cds.String", + "@mandatory": true + }, + "message": { + "type": "cds.String", + "@mandatory": true + }, + "target": { + "type": "cds.String" + }, + "details": { + "items": { + "elements": { + "code": { + "type": "cds.String", + "@mandatory": true + }, + "message": { + "type": "cds.String", + "@mandatory": true + }, + "target": { + "type": "cds.String" + } + } + } + }, + "innererror": { + "@description": "The structure of this object is service-specific", + "elements": {} + } + }, + "@mandatory": true + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type0": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "id_desc": { + "val": "id desc" + }, + "refComponent_id": {}, + "refComponent_id_desc": { + "val": "refComponent_id desc" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type1": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "refComponent_id": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type2": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "_": { + "val": "*" + }, + "refComponent": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type3": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "refComponent_id": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type4": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "_": { + "val": "*" + }, + "refComponent": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type5": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "refComponent1_id": {}, + "refComponent2_id": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type6": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "_": { + "val": "*" + }, + "refComponent1": {}, + "refComponent2": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type7": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "id_desc": { + "val": "id desc" + }, + "refComponent1_id": {}, + "refComponent1_id_desc": { + "val": "refComponent1_id desc" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type8": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "refComponent1_id": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type9": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "_": { + "val": "*" + }, + "refComponent1": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type10": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "refComponent1_id": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type11": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "_": { + "val": "*" + }, + "refComponent1": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type12": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "refComponent_id": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type13": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "_": { + "val": "*" + }, + "refComponent": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type14": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "id_desc": { + "val": "id desc" + }, + "refComponent1_id": {}, + "refComponent1_id_desc": { + "val": "refComponent1_id desc" + }, + "refComponent2_id": {}, + "refComponent2_id_desc": { + "val": "refComponent2_id desc" + } + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type15": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "refComponent1_id": {}, + "refComponent2_id": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type16": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "_": { + "val": "*" + }, + "refComponent1": {}, + "refComponent2": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type17": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "refComponent1_id": {}, + "refComponent2_id": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type18": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "_": { + "val": "*" + }, + "refComponent1": {}, + "refComponent2": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type19": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "refComponent1_id": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type20": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "_": { + "val": "*" + }, + "refComponent1": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type21": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "id": {}, + "refComponent1_id": {} + }, + "kind": "type" + }, + "Service.for.namespace.com.sap.test.MyService.anonymous.type22": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "_": { + "val": "*" + }, + "refComponent1": {} + }, + "kind": "type" + } + }, + "meta": { + "creator": "cds-import-openapi" + } + } \ No newline at end of file diff --git a/test/lib/import/output/wizard-world.csn b/test/lib/import/output/wizard-world.csn new file mode 100644 index 0000000..372b27a --- /dev/null +++ b/test/lib/import/output/wizard-world.csn @@ -0,0 +1,527 @@ +{ + "definitions": { + "WizardWorldApi": { + "kind": "service", + "@Capabilities.BatchSupported": false, + "@Capabilities.KeyAsSegmentSupported": true, + "@Core.Description": "WizardWorldApi", + "@Core.SchemaVersion": "1.0.1" + }, + "WizardWorldApi.Elixirs": { + "kind": "function", + "params": { + "Name": { + "type": "cds.String", + "@openapi.in": "query" + }, + "Difficulty": { + "type": "WizardWorldApi_types.ElixirDifficulty", + "@openapi.in": "query" + }, + "Ingredient": { + "type": "cds.String", + "@openapi.in": "query" + }, + "InventorFullName": { + "type": "cds.String", + "@openapi.in": "query" + }, + "Manufacturer": { + "type": "cds.String", + "@openapi.in": "query" + } + }, + "@Common.Label": "Elixirs", + "@openapi.path": "/Elixirs", + "returns": { + "items": { + "type": "WizardWorldApi_types.ElixirDto" + } + } + }, + "WizardWorldApi.Elixirs_": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@openapi.in": "path" + } + }, + "@Common.Label": "Elixirs", + "@openapi.path": "/Elixirs/{id}", + "returns": { + "type": "WizardWorldApi_types.ElixirDto" + } + }, + "WizardWorldApi.Feedback_post": { + "kind": "action", + "params": { + "body": { + "type": "WizardWorldApi_types.SendFeedbackCommand", + "@openapi.in": "body" + } + }, + "@Common.Label": "Feedback", + "@openapi.path": "/Feedback", + "returns": { + "type": "WizardWorldApi_types.Unit" + } + }, + "WizardWorldApi.Houses": { + "kind": "function", + "params": { + "query": { + "type": "WizardWorldApi_types.GetHousesQuery", + "@openapi.in": "query" + } + }, + "@Common.Label": "Houses", + "@openapi.path": "/Houses", + "returns": { + "items": { + "type": "WizardWorldApi_types.HouseDto" + } + } + }, + "WizardWorldApi.Houses_": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@openapi.in": "path" + } + }, + "@Common.Label": "Houses", + "@openapi.path": "/Houses/{id}", + "returns": { + "type": "WizardWorldApi_types.HouseDto" + } + }, + "WizardWorldApi.Ingredients": { + "kind": "function", + "params": { + "Name": { + "type": "cds.String", + "@openapi.in": "query" + } + }, + "@Common.Label": "Ingredients", + "@openapi.path": "/Ingredients", + "returns": { + "items": { + "type": "WizardWorldApi_types.IngredientDto" + } + } + }, + "WizardWorldApi.Ingredients_": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@openapi.in": "path" + } + }, + "@Common.Label": "Ingredients", + "@openapi.path": "/Ingredients/{id}", + "returns": { + "type": "WizardWorldApi_types.IngredientDto" + } + }, + "WizardWorldApi.Spells": { + "kind": "function", + "params": { + "Name": { + "type": "cds.String", + "@openapi.in": "query" + }, + "Type": { + "type": "WizardWorldApi_types.SpellType", + "@openapi.in": "query" + }, + "Incantation": { + "type": "cds.String", + "@openapi.in": "query" + } + }, + "@Common.Label": "Spells", + "@openapi.path": "/Spells", + "returns": { + "items": { + "type": "WizardWorldApi_types.SpellDto" + } + } + }, + "WizardWorldApi.Spells_": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@openapi.in": "path" + } + }, + "@Common.Label": "Spells", + "@openapi.path": "/Spells/{id}", + "returns": { + "type": "WizardWorldApi_types.SpellDto" + } + }, + "WizardWorldApi.Wizards": { + "kind": "function", + "params": { + "FirstName": { + "type": "cds.String", + "@openapi.in": "query" + }, + "LastName": { + "type": "cds.String", + "@openapi.in": "query" + } + }, + "@Common.Label": "Wizards", + "@openapi.path": "/Wizards", + "returns": { + "items": { + "type": "WizardWorldApi_types.WizardDto" + } + } + }, + "WizardWorldApi.Wizards_": { + "kind": "function", + "params": { + "id": { + "type": "cds.UUID", + "@openapi.in": "path" + } + }, + "@Common.Label": "Wizards", + "@openapi.path": "/Wizards/{id}", + "returns": { + "type": "WizardWorldApi_types.WizardDto" + } + }, + "WizardWorldApi_types.ElixirDifficulty": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "Unknown": {}, + "Advanced": {}, + "Moderate": {}, + "Beginner": {}, + "OrdinaryWizardingLevel": {}, + "OneOfAKind": {} + }, + "kind": "type" + }, + "WizardWorldApi_types.ElixirDto": { + "elements": { + "id": { + "type": "cds.UUID" + }, + "name": { + "type": "cds.String" + }, + "effect": { + "type": "cds.String" + }, + "sideEffects": { + "type": "cds.String" + }, + "characteristics": { + "type": "cds.String" + }, + "time": { + "type": "cds.String" + }, + "difficulty": { + "type": "WizardWorldApi_types.ElixirDifficulty" + }, + "ingredients": { + "items": { + "type": "WizardWorldApi_types.IngredientDto" + } + }, + "inventors": { + "items": { + "type": "WizardWorldApi_types.ElixirInventorDto" + } + }, + "manufacturer": { + "type": "cds.String" + } + }, + "kind": "type" + }, + "WizardWorldApi_types.ElixirInventorDto": { + "elements": { + "id": { + "type": "cds.UUID" + }, + "firstName": { + "type": "cds.String" + }, + "lastName": { + "type": "cds.String" + } + }, + "kind": "type" + }, + "WizardWorldApi_types.FeedbackType": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "General": {}, + "Bug": {}, + "DataError": {}, + "Suggestion": {} + }, + "kind": "type" + }, + "WizardWorldApi_types.GetHousesQuery": { + "elements": {}, + "kind": "type" + }, + "WizardWorldApi_types.HouseDto": { + "elements": { + "id": { + "type": "cds.UUID" + }, + "name": { + "type": "cds.String" + }, + "houseColours": { + "type": "cds.String" + }, + "founder": { + "type": "cds.String" + }, + "animal": { + "type": "cds.String" + }, + "element": { + "type": "cds.String" + }, + "ghost": { + "type": "cds.String" + }, + "commonRoom": { + "type": "cds.String" + }, + "heads": { + "items": { + "type": "WizardWorldApi_types.HouseHeadDto" + } + }, + "traits": { + "items": { + "type": "WizardWorldApi_types.TraitDto" + } + } + }, + "kind": "type" + }, + "WizardWorldApi_types.HouseHeadDto": { + "elements": { + "id": { + "type": "cds.UUID" + }, + "firstName": { + "type": "cds.String" + }, + "lastName": { + "type": "cds.String" + } + }, + "kind": "type" + }, + "WizardWorldApi_types.IngredientDto": { + "elements": { + "id": { + "type": "cds.UUID" + }, + "name": { + "type": "cds.String" + } + }, + "kind": "type" + }, + "WizardWorldApi_types.SendFeedbackCommand": { + "elements": { + "feedbackType": { + "type": "WizardWorldApi_types.FeedbackType" + }, + "feedback": { + "type": "cds.String" + }, + "entityId": { + "type": "cds.UUID" + } + }, + "kind": "type" + }, + "WizardWorldApi_types.SpellDto": { + "elements": { + "id": { + "type": "cds.UUID" + }, + "name": { + "type": "cds.String" + }, + "incantation": { + "type": "cds.String" + }, + "effect": { + "type": "cds.String" + }, + "canBeVerbal": { + "type": "cds.Boolean" + }, + "type": { + "type": "WizardWorldApi_types.SpellType" + }, + "light": { + "type": "WizardWorldApi_types.SpellLight" + }, + "creator": { + "type": "cds.String" + } + }, + "kind": "type" + }, + "WizardWorldApi_types.SpellLight": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "None": {}, + "Blue": {}, + "IcyBlue": {}, + "Red": {}, + "Gold": {}, + "Purple": {}, + "Transparent": {}, + "White": {}, + "Green": {}, + "Orange": {}, + "Yellow": {}, + "BrightBlue": {}, + "Pink": {}, + "Violet": {}, + "BlueishWhite": {}, + "Silver": {}, + "Scarlet": {}, + "Fire": {}, + "FieryScarlet": {}, + "Grey": {}, + "DarkRed": {}, + "Turquoise": {}, + "PsychedelicTransparentWave": {}, + "BrightYellow": {}, + "BlackSmoke": {} + }, + "kind": "type" + }, + "WizardWorldApi_types.SpellType": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "None": {}, + "Charm": {}, + "Conjuration": {}, + "Spell": {}, + "Transfiguration": {}, + "HealingSpell": {}, + "DarkCharm": {}, + "Jinx": {}, + "Curse": {}, + "MagicalTransportation": {}, + "Hex": {}, + "CounterSpell": {}, + "DarkArts": {}, + "CounterJinx": {}, + "CounterCharm": {}, + "Untransfiguration": {}, + "BindingMagicalContract": {}, + "Vanishment": {} + }, + "kind": "type" + }, + "WizardWorldApi_types.TraitDto": { + "elements": { + "id": { + "type": "cds.UUID" + }, + "name": { + "type": "WizardWorldApi_types.TraitName" + } + }, + "kind": "type" + }, + "WizardWorldApi_types.TraitName": { + "type": "cds.String", + "@assert.range": true, + "enum": { + "None": {}, + "Courage": {}, + "Bravery": {}, + "Determination": {}, + "Daring": {}, + "Nerve": {}, + "Chivalary": {}, + "Hardworking": {}, + "Patience": {}, + "Fairness": {}, + "Just": {}, + "Loyalty": {}, + "Modesty": {}, + "Wit": {}, + "Learning": {}, + "Wisdom": {}, + "Acceptance": {}, + "Inteligence": {}, + "Creativity": {}, + "Resourcefulness": {}, + "Pride": {}, + "Cunning": {}, + "Ambition": {}, + "Selfpreservation": {} + }, + "kind": "type" + }, + "WizardWorldApi_types.Unit": { + "elements": {}, + "kind": "type" + }, + "WizardWorldApi_types.WizardDto": { + "elements": { + "elixirs": { + "items": { + "type": "WizardWorldApi_types.WizardElixirDto" + } + }, + "id": { + "type": "cds.UUID" + }, + "firstName": { + "type": "cds.String" + }, + "lastName": { + "type": "cds.String" + } + }, + "kind": "type" + }, + "WizardWorldApi_types.WizardElixirDto": { + "elements": { + "id": { + "type": "cds.UUID" + }, + "name": { + "type": "cds.String" + } + }, + "kind": "type" + } + }, + "meta": { + "creator": "cds-import-openapi" + } +} \ No newline at end of file diff --git a/test/lib/import/utilities.test.js b/test/lib/import/utilities.test.js new file mode 100644 index 0000000..21f4689 --- /dev/null +++ b/test/lib/import/utilities.test.js @@ -0,0 +1,58 @@ +const { describe, it } = require('node:test') +const assert = require('node:assert') + +const { cdsName, nameFromPath, pathAndMethod } = require('../../../lib/import/utilities') + +describe('Utilities', () => { + it('cdsName', () => { + assert.equal(cdsName(''), '_', 'empty') + assert.equal(cdsName(42), '_42', 'number') + assert.equal(cdsName('0stuff'), '_0stuff', 'start with digit') + assert.equal(cdsName('_foo'), '_foo', 'start with underscore') + assert.equal(cdsName('foo-bar'), 'foo_bar', 'with dash') + }) + + it('nameFromPath', () => { + assert.equal(nameFromPath('/', 'get'), '_root', 'root-get') + assert.equal(nameFromPath('/', 'post'), '_root_post', 'root-post') + + assert.equal(nameFromPath('/root', 'get'), 'root', 'root-get') + assert.equal(nameFromPath('/root', 'post'), 'root_post', 'root-post') + + assert.equal(nameFromPath('/root/{id}', 'get'), 'root_', 'root-get') + assert.equal(nameFromPath('/root/{id}', 'post'), 'root__post', 'root-post') + + assert.equal(nameFromPath('/{var}', 'get'), '_', 'root-get') + assert.equal(nameFromPath('/{var}', 'post'), '__post', 'root-post') + + assert.equal(nameFromPath('/{var}/{var2}', 'get'), '__', 'root-get') + assert.equal(nameFromPath('/{var}/{var2}', 'post'), '___post', 'root-post') + + assert.equal(nameFromPath('/foo', 'get'), 'foo', 'one segment') + assert.equal(nameFromPath('/foo', 'post'), 'foo_post', 'one segment') + + assert.equal(nameFromPath('/foo/bar', 'get'), 'foo_bar', 'two segments') + assert.equal(nameFromPath('/foo/bar', 'post'), 'foo_bar_post', 'two segments') + + assert.equal(nameFromPath('/foo/{id}', 'get'), 'foo_', 'one segment and key segment') + assert.equal(nameFromPath('/foo/{id}', 'post'), 'foo__post', 'one segment and key segment') + }) +}) + +describe('pathAndMethod', () => { + it('function kind returns GET', () => { + const { path, method } = pathAndMethod({ kind: 'function', '@openapi.path': '/foo' }) + assert.equal(method, 'GET') + assert.equal(path, '/foo') + }) + + it('action with explicit method returns it', () => { + const { method } = pathAndMethod({ '@openapi.method': 'PUT', '@openapi.path': '/foo' }) + assert.equal(method, 'PUT') + }) + + it('action without explicit method defaults to POST', () => { + const { method } = pathAndMethod({ '@openapi.path': '/foo' }) + assert.equal(method, 'POST') + }) +})