diff --git a/CHANGELOG.md b/CHANGELOG.md index 02fe0f3..8b64651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - Back-reference navigation properties on child entities in compositions (the generated `parent` nav pointing back up) are no longer emitted in the OpenAPI read schema - Implicitly auto-exposed composition targets (annotated `@cds.autoexposed` by the CDS compiler) no longer generate top-level GET paths that CAP would reject with 405 - OpenAPI compilation is now pure: the input CSN is no longer mutated during compilation +- Services annotated with `@protocol: 'rest'` no longer include a `/$batch` path in the generated OpenAPI document ### Security ## [1.6.0] - 2026-08-04 diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index e65eb8d..8b7d781 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -9,6 +9,8 @@ const propertyUtil = require('./property-util'); const pluralize = require('pluralize') const DEBUG = cds.debug('openapi'); // Initialize cds.debug with the 'openapi' +const isODataProtocol = (protocol) => !protocol || protocol.startsWith('odata'); + //TODO // - Core.Example for complex types // - reduce number of loops over schemas @@ -88,7 +90,7 @@ const ER_ANNOTATIONS = Object.freeze( /** * Construct an OpenAPI description from a CSDL document * @param {CSDL} csdl CSDL document - * @param {{ url?: string, servers?: object, odataVersion?: string, scheme?: string, host?: string, basePath?: string, diagram?: boolean, maxLevels?: number, shortActionPaths?: boolean }} options Optional parameters + * @param {{ url?: string, servers?: object, odataVersion?: string, protocol?: string, scheme?: string, host?: string, basePath?: string, diagram?: boolean, maxLevels?: number, shortActionPaths?: boolean }} options Optional parameters * @return {*} OpenAPI description */ module.exports.csdl2openapi = function ( @@ -97,6 +99,7 @@ module.exports.csdl2openapi = function ( url: serviceRoot, servers: serversObject, odataVersion, + protocol, scheme = 'https', host = 'localhost', basePath = '/service-root', @@ -460,7 +463,7 @@ module.exports.csdl2openapi = function ( DEBUG?.(`Unrecognized entity container child: ${name}`); } }) - if (resources.length > 0) pathItemBatch(paths, container); + if (resources.length > 0 && isODataProtocol(protocol)) pathItemBatch(paths, container); return Object.keys(paths).sort().reduce((p, c) => (p[c] = paths[c], p), {}); } diff --git a/lib/compile/index.js b/lib/compile/index.js index d93acd4..3585800 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -94,6 +94,7 @@ function _getOpenApi(csdl, options, serviceName = "") { } sOptions.url = url; + sOptions.protocol = protocol; const openapi = csdl2openapi.csdl2openapi(csdl, sOptions); diff --git a/test/lib/compile/openapi.test.js b/test/lib/compile/openapi.test.js index e21e0b2..ad62e27 100644 --- a/test/lib/compile/openapi.test.js +++ b/test/lib/compile/openapi.test.js @@ -305,6 +305,19 @@ service CatalogService { '@protocol must not be mutated by toOpenApi'); }); + test('REST service does not include /$batch path', () => { + const csn = cds.compile.to.csn(` + @path: '/rest/v1/myRestAPI' + @protocol: 'rest' + service MyRestAPI { + entity Items { key ID : UUID; } + }` + ); + const openapi = toOpenApi(csn); + assert.strictEqual(openapi.paths?.['/$batch'], undefined, + '/$batch must not be present in OpenAPI output for a REST service'); + }); + test('options: Multiple servers', () => { const csn = cds.compile.to.csn(` service A {entity E { key ID : UUID; };};`