From 725ff6d63c6ae094784f5432a18f67f9139244e3 Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Wed, 23 Sep 2026 10:21:53 +0200 Subject: [PATCH 1/3] fix: suppress $batch path for REST services --- CHANGELOG.md | 1 + lib/compile/csdl2openapi.js | 7 ++++++- lib/compile/index.js | 1 + test/lib/compile/openapi.test.js | 13 +++++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9cbe10..5ceaa3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). - Entities that are transitively autoexposed and should still be considered readonly, do not generate documentation for write endpoints anymore - 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 78b844d..75a63ee 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -8,6 +8,10 @@ const { Diagram } = require('./diagram') const pluralize = require('pluralize') const DEBUG = cds.debug('openapi'); // Initialize cds.debug with the 'openapi' +function isODataProtocol(protocol) { + return !protocol || protocol.startsWith('odata'); +} + //TODO // - Core.Example for complex types // - reduce number of loops over schemas @@ -96,6 +100,7 @@ module.exports.csdl2openapi = function ( url: serviceRoot, servers: serversObject, odataVersion, + protocol, scheme = 'https', host = 'localhost', basePath = '/service-root', @@ -458,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; };};` From 8b0a0de2db3c5cce33ca3d5b229202ea57693dbd Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Wed, 23 Sep 2026 10:21:53 +0200 Subject: [PATCH 2/3] fix: suppress $batch path for REST services --- lib/compile/csdl2openapi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 75a63ee..4ed8909 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -91,7 +91,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 ( From bd3b00f0e0265ab9cd2d419c3911c1c2e1fdecef Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Wed, 23 Sep 2026 12:16:13 +0200 Subject: [PATCH 3/3] Crisper --- lib/compile/csdl2openapi.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index baaf90f..8b7d781 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -9,9 +9,7 @@ const propertyUtil = require('./property-util'); const pluralize = require('pluralize') const DEBUG = cds.debug('openapi'); // Initialize cds.debug with the 'openapi' -function isODataProtocol(protocol) { - return !protocol || protocol.startsWith('odata'); -} +const isODataProtocol = (protocol) => !protocol || protocol.startsWith('odata'); //TODO // - Core.Example for complex types