From 8b037a6a9f0ee42cf048ec37a73020c69074c9f9 Mon Sep 17 00:00:00 2001 From: Lukas Schwabe Date: Thu, 20 Aug 2026 16:42:43 +0200 Subject: [PATCH 1/4] fix: use OData V4 path for OpenAPI server URL if no protocol is set to match @sap/cds default behavior Signed-off-by: Lukas Schwabe --- lib/compile/index.js | 2 +- test/lib/compile/openapi.test.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/compile/index.js b/lib/compile/index.js index 1bfb40f..6dabd6d 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -156,7 +156,7 @@ function _getProtocols(csdl, csn, odataVersion) { protocols.push("odata"); } else if (!service["@protocol"]) { - protocols.push("rest"); //taking rest as default in case no relevant protocol is there + protocols.push("odata"); // matches cds's default protocol when none is annotated } else if (service["@protocol"] === "none") { // if @protocol is 'none' then throw an error throw new Error( diff --git a/test/lib/compile/openapi.test.js b/test/lib/compile/openapi.test.js index 79e5374..24961dd 100644 --- a/test/lib/compile/openapi.test.js +++ b/test/lib/compile/openapi.test.js @@ -262,6 +262,21 @@ service CatalogService { assert(openapi.servers[0].url.includes('odata')); }); + test('default service without @protocol renders server URL with odata/v4', () => { + const csn = cds.compile.to.csn(` + @path: 'catalog' + service CatalogService { + entity Books { + key ID : Integer; + title : String; + } + }` + ); + const openapi = toOpenApi(csn); + assert(openapi.servers[0].url.includes('odata/v4')); + assert(!openapi.servers[0].url.includes('rest')); + }); + test('options: Multiple servers', () => { const csn = cds.compile.to.csn(` service A {entity E { key ID : UUID; };};` From db8cc224284dd8e79c3ba55255997f39be6628f1 Mon Sep 17 00:00:00 2001 From: Lukas Schwabe Date: Fri, 21 Aug 2026 09:20:12 +0200 Subject: [PATCH 2/4] chore: adjust changelog.md to fit the fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4285a52..267c2a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Deprecated ### Removed ### Fixed +- set `odata` as the service protocol if no protocol is set to match the default behavior of `@sap/cds` ### Security ## [1.6.0] - 2026-08-04 From 7c32c85d938980b7e57e8b52e7299761dda05457 Mon Sep 17 00:00:00 2001 From: Lukas Schwabe Date: Tue, 1 Sep 2026 09:37:57 +0200 Subject: [PATCH 3/4] fix: implement escape hatch to breaking change by providing defaultProtocol config option --- CHANGELOG.md | 3 ++- lib/compile/index.js | 11 ++++++----- test/lib/compile/openapi.test.js | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 267c2a3..8a580d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ## [Unreleased] ### Added +- Added `defaultProtocol` configuration option as fallback if no config is provided. ### Changed +- set `odata` as the service protocol if no protocol is set to match the default behavior of `@sap/cds`. To restore older behavior by providing `cds.env.openapi.defaultProtocol = "rest"`, e.g. via `cds` section in the package.json ### Deprecated ### Removed ### Fixed -- set `odata` as the service protocol if no protocol is set to match the default behavior of `@sap/cds` ### Security ## [1.6.0] - 2026-08-04 diff --git a/lib/compile/index.js b/lib/compile/index.js index 6dabd6d..09753f3 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -51,7 +51,7 @@ function processor(csn, options = {}) { } const openApiOptions = toOpenApiOptions(csdl, csn, options); const serviceName = csdl.$EntityContainer.replace(/\.[^.]+$/, ""); - openApiDocs = _getOpenApi(csdl, openApiOptions,serviceName); + openApiDocs = _getOpenApi(csdl, openApiOptions, serviceName); return Object.keys(openApiDocs).length === 1 ? openApiDocs[serviceName] : _iterate(openApiDocs); @@ -118,11 +118,12 @@ function toOpenApiOptions(csdl, csn, options = {}) { } const envOptions = cds.env.openapi instanceof Object && !Array.isArray(cds.env.openapi) ? cds.env.openapi : {}; + envOptions.defaultProtocol = envOptions.defaultProtocol ?? "odata"; const fileOptions = _readConfigFile(callerOptions["config-file"]); const result = { ...envOptions, ...fileOptions, ...callerOptions }; delete result["config-file"]; - const protocols = _getProtocols(csdl, csn, result.odataVersion); + const protocols = _getProtocols(csdl, csn, result.odataVersion, result.defaultProtocol); if (result.url) { const servicePaths = _servicePath(csdl, csn, protocols); @@ -143,20 +144,20 @@ function toOpenApiOptions(csdl, csn, options = {}) { return result; } -function _getProtocols(csdl, csn, odataVersion) { +function _getProtocols(csdl, csn, odataVersion, defaultProtocol) { if (csdl.$EntityContainer) { const serviceName = csdl.$EntityContainer.replace(/\.[^.]+$/, ""); const service = csn.definitions[serviceName]; const protocols = []; if(odataVersion === "4.01"){ - protocols.push("rest"); + protocols.push("odata"); } else if(odataVersion === "4.0"){ protocols.push("odata"); } else if (!service["@protocol"]) { - protocols.push("odata"); // matches cds's default protocol when none is annotated + protocols.push(defaultProtocol); // matches cds's default protocol when none is annotated } else if (service["@protocol"] === "none") { // if @protocol is 'none' then throw an error throw new Error( diff --git a/test/lib/compile/openapi.test.js b/test/lib/compile/openapi.test.js index 24961dd..a74f7be 100644 --- a/test/lib/compile/openapi.test.js +++ b/test/lib/compile/openapi.test.js @@ -277,6 +277,21 @@ service CatalogService { assert(!openapi.servers[0].url.includes('rest')); }); + test('defaultProtocol overrides OData for a service without @protocol', () => { + const csn = cds.compile.to.csn(` + @path: 'catalog' + service CatalogService { + entity Books { + key ID : Integer; + title : String; + } + }` + ); + const openapi = toOpenApi(csn, { 'openapi:defaultProtocol': 'rest' }); + assert(openapi.servers[0].url.includes('rest/catalog')); + assert(!openapi.servers[0].url.includes('odata')); + }); + test('options: Multiple servers', () => { const csn = cds.compile.to.csn(` service A {entity E { key ID : UUID; };};` From 4e25409ed8e168806c68d16c4facb608585f0eab Mon Sep 17 00:00:00 2001 From: cronossclk Date: Wed, 2 Sep 2026 08:33:56 +0200 Subject: [PATCH 4/4] Update CHANGELOG.md Co-authored-by: Daniel O'Grady <103028279+daogrady@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efa8d5d..1d9ed33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Added - Added `defaultProtocol` configuration option as fallback if no config is provided. ### Changed -- set `odata` as the service protocol if no protocol is set to match the default behavior of `@sap/cds`. To restore older behavior by providing `cds.env.openapi.defaultProtocol = "rest"`, e.g. via `cds` section in the package.json +- set `odata` as the service protocol if no protocol is set to match the default behavior of `@sap/cds`. Provide `cds.env.openapi.defaultProtocol = "rest"` to restore old behavior. ### Deprecated ### Removed ### Fixed