diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d4f2b8..157b174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.27.0 + +### Changed + +- OpenAPI 3.1.x: use `examples` in Schema Objects instead of the deprecated `example` + ## 0.26.1 - 2024-07-09 ### Fixed diff --git a/lib/cli.js b/lib/cli.js index 41db118..68eb7e5 100755 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,11 +1,12 @@ #!/usr/bin/env node "use strict"; -const csdl = require("odata-csdl"); -const lib = require("./csdl2openapi"); -const fs = require("fs"); +const { csdl2openapi } = require("./csdl2openapi"); +const { xml2json } = require("odata-csdl"); +const { createWriteStream, existsSync, readFileSync } = require("node:fs"); +const { Readable } = require("node:stream"); const { parseArguments } = require("./cliParts"); -const { stringifyStream } = require("@discoveryjs/json-ext"); +const { stringifyChunked } = require("@discoveryjs/json-ext"); const args = parseArguments(process.argv.slice(2)); if (args.unknown) console.error(args.unknown); @@ -13,23 +14,23 @@ if (args.usage) console.log(args.usage); else convert(args); function convert(args) { - if (!fs.existsSync(args.source)) { + if (!existsSync(args.source)) { console.error("Source file not found: " + args.source); return; } - const text = fs.readFileSync(args.source, "utf8"); - const json = text.startsWith("<") ? csdl.xml2json(text) : JSON.parse(text); + const text = readFileSync(args.source, "utf8"); + const json = text.startsWith("<") ? xml2json(text) : JSON.parse(text); console.log(args.target); const messages = []; - const openapi = lib.csdl2openapi(json, { messages, ...args.options }); + const openapi = csdl2openapi(json, { messages, ...args.options }); - stringifyStream(openapi, null, args.options.pretty ? 4 : 0).pipe( - fs.createWriteStream(args.target), - ); + Readable.from( + stringifyChunked(openapi, null, args.options.pretty ? 4 : 0), + ).pipe(createWriteStream(args.target)); if (messages.length > 0) console.dir(messages); } diff --git a/lib/csdl2openapi.js b/lib/csdl2openapi.js index 546e7d6..90bcf2a 100644 --- a/lib/csdl2openapi.js +++ b/lib/csdl2openapi.js @@ -632,10 +632,8 @@ module.exports.csdl2openapi = function ( const deltaSupported = element[voc.Capabilities.ChangeTracking] && element[voc.Capabilities.ChangeTracking].Supported; - if (!byKey && deltaSupported) { - operation.responses[200].content["application/json"].schema.properties[ - "@odata.deltaLink" - ] = { + if (collection && deltaSupported) { + const deltaLinkSchema = { type: "string", example: basePath + @@ -643,6 +641,13 @@ module.exports.csdl2openapi = function ( name + "?$deltatoken=opaque server-generated token for fetching the delta", }; + if (oas31) { + deltaLinkSchema.examples = [deltaLinkSchema.example]; + delete deltaLinkSchema.example; + } + operation.responses[200].content["application/json"].schema.properties[ + "@odata.deltaLink" + ] = deltaLinkSchema; } if (target && sourceName != targetName) operation.tags.push(targetName); @@ -2724,6 +2729,11 @@ module.exports.csdl2openapi = function ( } } + if (oas31 && s.example !== undefined) { + s.examples = [s.example]; + delete s.example; + } + if (typeof element[voc.Validation.Maximum] === "number") { if (s.$ref) s = { allOf: [s] }; s.maximum = element[voc.Validation.Maximum]; diff --git a/package-lock.json b/package-lock.json index d70504d..17425f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "odata-openapi", - "version": "0.26.1", + "version": "0.27.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "odata-openapi", - "version": "0.26.1", + "version": "0.27.0", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { - "@discoveryjs/json-ext": "^0.5.7", + "@discoveryjs/json-ext": "^0.6.0", "odata-csdl": "^0.10.1" }, "bin": { @@ -17,11 +17,11 @@ }, "devDependencies": { "@apidevtools/openapi-schemas": "^2.1.0", - "ajv": "^8.16.0", + "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "c8": "^10.1.2", - "eslint": "^9.6.0", + "eslint": "^9.7.0", "mocha": "^10.6.0" }, "engines": { @@ -46,12 +46,12 @@ "license": "MIT" }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.0.tgz", + "integrity": "sha512-ggk8A6Y4RxpOPaCER/yl1sYtcZ1JfFBdHR6it/e2IolmV3VXSaFov2hqmWUdh9dXgpMprSg3xUvkGJDfR5sc/w==", "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=14.17.0" } }, "node_modules/@eslint-community/eslint-utils": { @@ -157,9 +157,9 @@ "license": "MIT" }, "node_modules/@eslint/js": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.6.0.tgz", - "integrity": "sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.7.0.tgz", + "integrity": "sha512-ChuWDQenef8OSFnvuxv0TCVxEwmu3+hPNKvM9B34qpM0rDRbjL8t5QkQeHHeAfsKQjuH9wS82WeCi1J/owatng==", "dev": true, "license": "MIT", "engines": { @@ -425,16 +425,16 @@ } }, "node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -853,17 +853,17 @@ } }, "node_modules/eslint": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.6.0.tgz", - "integrity": "sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.7.0.tgz", + "integrity": "sha512-FzJ9D/0nGiCGBf8UXO/IGLTgLVzIxze1zpfA8Ton2mjLovXdAPlYDv+MQDcqj3TmrhAGYfOpz9RfR+ent0AgAw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", + "@eslint-community/regexpp": "^4.11.0", "@eslint/config-array": "^0.17.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.6.0", + "@eslint/js": "9.7.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -872,7 +872,7 @@ "cross-spawn": "^7.0.2", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.1", + "eslint-scope": "^8.0.2", "eslint-visitor-keys": "^4.0.0", "espree": "^10.1.0", "esquery": "^1.5.0", @@ -905,9 +905,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", - "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -1043,6 +1043,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "dev": true, + "license": "MIT" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", diff --git a/package.json b/package.json index ec3aa14..16c58ab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "odata-openapi", - "version": "0.26.1", + "version": "0.27.0", "description": "Convert OData CSDL XML or CSDL JSON to OpenAPI", "homepage": "https://github.com/oasis-tcs/odata-openapi/blob/master/lib/README.md", "bugs": "https://github.com/oasis-tcs/odata-openapi/issues", @@ -20,16 +20,16 @@ "main": "./lib/csdl2openapi.js", "exports": "./lib/csdl2openapi.js", "dependencies": { - "@discoveryjs/json-ext": "^0.5.7", + "@discoveryjs/json-ext": "^0.6.0", "odata-csdl": "^0.10.1" }, "devDependencies": { "@apidevtools/openapi-schemas": "^2.1.0", - "ajv": "^8.16.0", + "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "c8": "^10.1.2", - "eslint": "^9.6.0", + "eslint": "^9.7.0", "mocha": "^10.6.0" }, "scripts": { diff --git a/test/csdl2openapi.test.js b/test/csdl2openapi.test.js index cf72923..1baa1a0 100644 --- a/test/csdl2openapi.test.js +++ b/test/csdl2openapi.test.js @@ -1845,6 +1845,7 @@ describe("Edge cases", function () { $Reference: { dummy: { $Include: [ + { $Namespace: "Org.OData.Capabilities.V1", $Alias: "Capabilities" }, { $Namespace: "Org.OData.Core.V1", $Alias: "Core" }, { $Namespace: "Org.OData.Validation.V1", $Alias: "Validation" }, ], @@ -1853,7 +1854,15 @@ describe("Edge cases", function () { $EntityContainer: "oas31.Container", oas31: { Container: { - sing: { $Type: "oas31.single" }, + set: { + $Type: "oas31.single", + $Collection: true, + "@Capabilities.ChangeTracking": { Supported: true }, + }, + sing: { + $Type: "oas31.single", + "@Capabilities.ChangeTracking": { Supported: true }, // has currently no effect + }, }, single: { $Kind: "EntityType", @@ -1904,7 +1913,7 @@ describe("Edge cases", function () { const properties = { binary: { type: "string", contentEncoding: "base64url" }, stream: { type: "string", contentEncoding: "base64url" }, - date: { type: "string", format: "date", example: "2017-04-13" }, // example is deprecated but still allowed + date: { type: "string", format: "date", examples: ["2017-04-13"] }, nullableString: { type: ["string", "null"] }, primitive: { type: ["boolean", "number", "string"] }, ref: { $ref: "#/components/schemas/oas31.typeDef" }, @@ -1922,7 +1931,7 @@ describe("Edge cases", function () { }, refEx: { allOf: [{ $ref: "#/components/schemas/oas31.typeDef" }], - example: 11, + examples: [11], }, refMax: { allOf: [{ $ref: "#/components/schemas/oas31.typeDef" }], @@ -1942,19 +1951,19 @@ describe("Edge cases", function () { exclusiveMin: { type: ["integer", "string"], format: "int64", - example: "42", + examples: ["42"], exclusiveMinimum: 0, }, max: { type: ["number", "string", "null"], format: "decimal", - example: 0, + examples: [0], maximum: 42, }, exclusiveMax: { type: "number", format: "double", - example: 3.14, + examples: [3.14], exclusiveMaximum: 42, }, }; @@ -1967,5 +1976,16 @@ describe("Edge cases", function () { properties, "Schemas", ); + assert.deepStrictEqual( + openapi.paths["/set"].get.responses[200].content["application/json"] + .schema.properties["@odata.deltaLink"], + { + type: "string", + examples: [ + "/service-root/set?$deltatoken=opaque server-generated token for fetching the delta", + ], + }, + "delta link", + ); }); });