Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).

### Added
- Added `defaultProtocol` configuration option as fallback if no config is provided.
- Collection responses now include the optional `@odata.nextLink` / `@nextLink` property in their schema, documenting server-driven paging as per the OData specification.
### Changed
- 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.
- ER diagrams are now rendered using the yuml.me v1 API (`app.yuml.me`), avoiding the 'please migrate' image. The diagram and legend are displayed as clickable SVG images.
Expand Down
15 changes: 12 additions & 3 deletions lib/compile/csdl2openapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
* @param {{ url?: string, servers?: object, odataVersion?: string, scheme?: string, host?: string, basePath?: string, diagram?: boolean, maxLevels?: number, shortActionPaths?: boolean }} options Optional parameters
* @return {*} OpenAPI description
*/
module.exports.csdl2openapi = function (

Check warning on line 94 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / lint

Function has a complexity of 22. Maximum allowed is 15

Check warning on line 94 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 20

Function has a complexity of 22. Maximum allowed is 15

Check warning on line 94 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 24

Function has a complexity of 22. Maximum allowed is 15
csdl,
{
url: serviceRoot,
Expand Down Expand Up @@ -478,7 +478,7 @@
* @param {number} options.level Number of navigation segments so far
* @param {string} options.navigationPath Path for finding navigation restrictions
*/
function pathItems({ paths, prefix, prefixParameters, element, root, sourceName, targetName, target, level, navigationPath }) {

Check warning on line 481 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / lint

Function 'pathItems' has a complexity of 19. Maximum allowed is 15

Check warning on line 481 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 20

Function 'pathItems' has a complexity of 19. Maximum allowed is 15

Check warning on line 481 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 24

Function 'pathItems' has a complexity of 19. Maximum allowed is 15
const name = prefix.substring(prefix.lastIndexOf('/') + 1);
const type = meta.modelElement(element.$Type);
const pathItem = {};
Expand Down Expand Up @@ -756,7 +756,7 @@
* @param {boolean} options.byKey Read by key
* @param {array} options.nonExpandable Non-expandable navigation properties
*/
function operationRead({ pathItem, element, name, sourceName, targetName, target, level, restrictions, byKey, nonExpandable }) {

Check warning on line 759 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / lint

Function 'operationRead' has a complexity of 30. Maximum allowed is 15

Check warning on line 759 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 20

Function 'operationRead' has a complexity of 30. Maximum allowed is 15

Check warning on line 759 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 24

Function 'operationRead' has a complexity of 30. Maximum allowed is 15
const targetRestrictions = target?.[meta.voc.Capabilities.ReadRestrictions];
const readRestrictions = restrictions.ReadRestrictions || targetRestrictions || {};
const readByKeyRestrictions = readRestrictions.ReadByKeyRestrictions;
Expand Down Expand Up @@ -1226,7 +1226,7 @@
* @param {*} options.restrictions Navigation property restrictions of navigation segment
* @param {boolean} [options.byKey=false] Update by key
*/
function operationUpdate({ pathItem, element, name, sourceName, target, level, restrictions, byKey = false }) {

Check warning on line 1229 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / lint

Function 'operationUpdate' has a complexity of 16. Maximum allowed is 15

Check warning on line 1229 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 20

Function 'operationUpdate' has a complexity of 16. Maximum allowed is 15

Check warning on line 1229 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 24

Function 'operationUpdate' has a complexity of 16. Maximum allowed is 15
const updateRestrictions = restrictions.UpdateRestrictions || target?.[meta.voc.Capabilities.UpdateRestrictions] || {};
const countRestrictions = target?.[meta.voc.Capabilities.CountRestrictions]?.Countable === false;
if (updateRestrictions.Updatable !== false && !element[meta.voc.Core.Immutable]) {
Expand Down Expand Up @@ -1317,7 +1317,7 @@
*/
function pathItemsForMediaStream({ paths, prefix, prefixParameters, type, name, sourceName }) {
if (type.$HasStream) {
const mediaTypes = type[meta.voc.Core.AcceptableMediaTypes]?.map(t => t['$EnumMember'] ?? t) ?? [];

Check warning on line 1320 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / lint

["$EnumMember"] is better written in dot notation

Check warning on line 1320 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 20

["$EnumMember"] is better written in dot notation

Check warning on line 1320 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 24

["$EnumMember"] is better written in dot notation
const contentTypes = mediaTypes.length > 0 ? mediaTypes : ['*/*'];
const mediaContent = Object.fromEntries(contentTypes.map(ct => [ct, { schema: { type: 'string', format: 'binary' } }]));
const lname = splitName(name);
Expand Down Expand Up @@ -1647,7 +1647,7 @@
* @param {string} options.sourceName Name of path source
* @param {*} [options.actionImport={}] Action import
*/
function pathItemAction({ paths, prefix, prefixParameters, actionName, overload, sourceName, actionImport = {} }) {

Check warning on line 1650 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / lint

Function 'pathItemAction' has a complexity of 18. Maximum allowed is 15

Check warning on line 1650 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 20

Function 'pathItemAction' has a complexity of 18. Maximum allowed is 15

Check warning on line 1650 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 24

Function 'pathItemAction' has a complexity of 18. Maximum allowed is 15
const name = actionName.indexOf('.') === -1 ? actionName : nameParts(actionName).name;
const pathItem = {
post: {
Expand Down Expand Up @@ -1737,7 +1737,7 @@
* @param {string} options.sourceName Name of path source
* @param {*} [options.functionImport={}] Function Import
*/
function pathItemFunction({ paths, prefix, prefixParameters, functionName, overload, sourceName, functionImport = {} }) {

Check warning on line 1740 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / lint

Function 'pathItemFunction' has a complexity of 16. Maximum allowed is 15

Check warning on line 1740 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 20

Function 'pathItemFunction' has a complexity of 16. Maximum allowed is 15

Check warning on line 1740 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 24

Function 'pathItemFunction' has a complexity of 16. Maximum allowed is 15
const name = functionName.indexOf('.') === -1 ? functionName : nameParts(functionName).name;
let parameters = overload.$Parameter || [];
if (overload.$IsBound) parameters = parameters.slice(1);
Expand All @@ -1746,7 +1746,7 @@

const implicitAliases = csdl.$Version > '4.0' || parameters.some(p => p[meta.voc.Core.OptionalParameter]);

parameters.forEach(p => {

Check warning on line 1749 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / lint

Arrow function has a complexity of 29. Maximum allowed is 15

Check warning on line 1749 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 20

Arrow function has a complexity of 29. Maximum allowed is 15

Check warning on line 1749 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 24

Arrow function has a complexity of 29. Maximum allowed is 15
const description = getDescriptionWithFallback(p);
/** @type {Parameter} */
const param = {
Expand Down Expand Up @@ -1908,7 +1908,15 @@
description
}
};
const CountPropertyObj = { [csdl.$Version > '4.0' ? '@count' : '@odata.count']: ref('count') };
const countProperty = { [csdl.$Version > '4.0' ? '@count' : '@odata.count']: ref('count') };
const nextLinkKey = csdl.$Version > '4.0' ? '@nextLink' : '@odata.nextLink';
const nextLinkProperty = {
[nextLinkKey]: {
type: 'string',
format: 'uri',
description: 'URL of the next page in server-driven paging, see [Server-driven Paging](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging)'
}
};
if (code != 204) {
const s = getSchema(type);
r[code].content = {
Expand All @@ -1921,8 +1929,9 @@
type: 'object',
title: `Collection of ${nameParts(type.$Type ? type.$Type : 'Edm.String').name}`,
properties: {
...(isCount && CountPropertyObj),
value: s
...(isCount && countProperty),
value: s,
...nextLinkProperty
}
};
}
Expand Down Expand Up @@ -2126,7 +2135,7 @@
* @param {string} options.suffix Suffix for read/create/update
* @return {*} Map of Schemas Objects
*/
function schemasForStructuredType({ schemas, qualifier, name, type, suffix }) {

Check warning on line 2138 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / lint

Function 'schemasForStructuredType' has a complexity of 18. Maximum allowed is 15

Check warning on line 2138 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 20

Function 'schemasForStructuredType' has a complexity of 18. Maximum allowed is 15

Check warning on line 2138 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 24

Function 'schemasForStructuredType' has a complexity of 18. Maximum allowed is 15
const schemaName = `${qualifier}.${name}${suffix}`;
const baseName = `${qualifier}.${name}`;
const isKey = keyMap(type);
Expand All @@ -2142,7 +2151,7 @@
const properties = propertiesOfStructuredType(type);
const expandRestrictions = type[meta.voc.Capabilities.ExpandRestrictions] ?? {};
const nonExpandableProperties = expandRestrictions.NonExpandableProperties ?? [];
Object.keys(properties).forEach(iName => {

Check warning on line 2154 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / lint

Arrow function has a complexity of 24. Maximum allowed is 15

Check warning on line 2154 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 20

Arrow function has a complexity of 24. Maximum allowed is 15

Check warning on line 2154 in lib/compile/csdl2openapi.js

View workflow job for this annotation

GitHub Actions / Node.js 24

Arrow function has a complexity of 24. Maximum allowed is 15
if (nonExpandableProperties.includes(iName)) return;

const property = properties[iName];
Expand Down
6 changes: 5 additions & 1 deletion scripts/regenerate.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ const specialOptions = {
basePath: '/V4/(S(cnbm44wtbc1v5bgrlek5lpcc))/TripPinServiceRW',
diagram: true
},
'autoexposed-texts': {}
'autoexposed-texts': {},
'autoexposed-direct': {},
'autoexposed-composition': {},
'autoexposed-codelist': {},
'back-reference': { diagram: false },
};

// Default options for all other test cases.
Expand Down
66 changes: 66 additions & 0 deletions test/lib/compile/csdl2openapi.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,11 @@ describe("Edge cases", () => {
maxLength: 20,
},
},
"@odata.nextLink": {
type: "string",
format: "uri",
description: "URL of the next page in server-driven paging, see [Server-driven Paging](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging)",
},
},
},
},
Expand Down Expand Up @@ -1260,6 +1265,11 @@ describe("Edge cases", () => {
//TODO:delta
},
},
"@nextLink": {
type: "string",
format: "uri",
description: "URL of the next page in server-driven paging, see [Server-driven Paging](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging)",
},
"@odata.deltaLink": {
example:
"/service-root/Set?$deltatoken=opaque server-generated token for fetching the delta",
Expand All @@ -1281,6 +1291,32 @@ describe("Edge cases", () => {
);
});

test("nextLink in collection response", () => {
const csdl = {
$EntityContainer: "this.Container",
this: {
ET: { $Kind: "EntityType", $Key: ["key"], key: {} },
Container: { Set: { $Type: "this.ET", $Collection: true } },
},
};
const v401 = lib.csdl2openapi(csdl, {});
const v401Props = v401.paths["/Set"].get.responses[200].content["application/json"].schema.properties;
assert.ok("@nextLink" in v401Props, "v4.01 should have @nextLink");
assert.deepStrictEqual(v401Props["@nextLink"], {
type: "string",
format: "uri",
description: "URL of the next page in server-driven paging, see [Server-driven Paging](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging)",
});
assert.ok(!("@odata.nextLink" in v401Props), "v4.01 must not emit @odata.nextLink");
assert.ok(!("@nextLink" in (v401.paths["/Set('{key}')"].get.responses[200].content["application/json"].schema.properties ?? {})),
"by-key response must not have @nextLink");

const v40 = lib.csdl2openapi(csdl, { odataVersion: "4.0" });
const v40Props = v40.paths["/Set"].get.responses[200].content["application/json"].schema.properties;
assert.ok("@odata.nextLink" in v40Props, "v4.0 should have @odata.nextLink");
assert.ok(!("@nextLink" in v40Props), "v4.0 must not emit @nextLink");
});

test("entity set and singleton with non-existing type", () => {
const csdl = {
$EntityContainer: "this.Container",
Expand Down Expand Up @@ -1328,6 +1364,11 @@ describe("Edge cases", () => {
$ref: "#/components/schemas/undefined.type_does_not_exist",
},
},
"@nextLink": {
type: "string",
format: "uri",
description: "URL of the next page in server-driven paging, see [Server-driven Paging](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging)",
},
},
},
},
Expand Down Expand Up @@ -1565,6 +1606,11 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
$ref: "#/components/schemas/this.derived",
},
},
"@nextLink": {
type: "string",
format: "uri",
description: "URL of the next page in server-driven paging, see [Server-driven Paging](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging)",
},
},
},
},
Expand Down Expand Up @@ -1756,6 +1802,11 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
$ref: "#/components/schemas/this.source",
},
},
"@nextLink": {
type: "string",
format: "uri",
description: "URL of the next page in server-driven paging, see [Server-driven Paging](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging)",
},
},
},
},
Expand Down Expand Up @@ -1923,6 +1974,11 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
$ref: "#/components/schemas/this.Category",
},
},
"@nextLink": {
type: "string",
format: "uri",
description: "URL of the next page in server-driven paging, see [Server-driven Paging](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging)",
},
},
},
},
Expand Down Expand Up @@ -2128,6 +2184,11 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
$ref: "#/components/schemas/this.thing",
},
},
"@nextLink": {
type: "string",
format: "uri",
description: "URL of the next page in server-driven paging, see [Server-driven Paging](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging)",
},
},
},
},
Expand Down Expand Up @@ -3451,6 +3512,11 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
$ref: "#/components/schemas/this.thing",
},
},
"@nextLink": {
type: "string",
format: "uri",
description: "URL of the next page in server-driven paging, see [Server-driven Paging](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging)",
},
},
},
},
Expand Down
Loading
Loading