From 37c04a820ae3d4547312c3ddbf773350420d948d Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Tue, 2 Jun 2026 07:55:25 +0200 Subject: [PATCH 1/6] Remove no-param-reassign rule --- eslint.config.mjs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 41e3431..e9a7d40 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -16,7 +16,6 @@ export default [ 'max-len': ['off'], 'complexity': ['warn', 15], 'max-params': ['warn', 4], - 'no-param-reassign': 'warn', 'prefer-const': 'warn', 'no-var': 'error', 'object-shorthand': 'warn', @@ -31,13 +30,13 @@ export default [ 'yoda': ['warn', 'never', { exceptRange: true }], // Natural comparison order, except ranges 'no-implicit-coercion': 'off', // Allow !!value, +str, etc. 'no-nested-ternary': 'off', // Allow with proper newlines - + // Error Prevention - inspired by Phoenix35/eslint-config 'no-throw-literal': 'error', 'no-eval': 'error', 'no-implied-eval': 'error', 'no-new-func': 'error', - + // Code Quality 'no-lonely-if': 'warn', 'no-useless-return': 'warn', From e02dc2cc4503db514174701ecf2651bf88c499ad Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Tue, 2 Jun 2026 07:56:26 +0200 Subject: [PATCH 2/6] Lint --- lib/compile/csdl2openapi.js | 4 ++-- lib/compile/index.js | 10 ++++------ test/lib/compile/openapi.test.js | 6 +++--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 75a34a2..4dd7830 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -106,7 +106,7 @@ module.exports.csdl2openapi = function ( ) { diagram = /** @type {unknown} */(diagram) !== "false" && !!diagram; // as preProcess below mutates the csdl, copy it before, to avoid side-effects on the caller side - csdl = JSON.parse(JSON.stringify(csdl)) + csdl = structuredClone(csdl) csdl.$Version = odataVersion ? odataVersion : '4.01' const meta = new CSDLMeta(csdl) serviceRoot = serviceRoot ?? (`${scheme}://${host}${basePath}`); @@ -122,7 +122,7 @@ module.exports.csdl2openapi = function ( Object.keys(entityContainer).forEach(element => { if (entityContainer[element].$Type) { const fullTypeName = entityContainer[element].$Type; - const type = fullTypeName.startsWith(serviceName + '.') + const type = fullTypeName.startsWith(`${serviceName}.`) ? fullTypeName.substring(serviceName.length + 1) : nameParts(fullTypeName).name; if ((csdl[serviceName]?.[type]?.['@cds.autoexpose'] || csdl[serviceName]?.[type]?.['@cds.autoexposed']) diff --git a/lib/compile/index.js b/lib/compile/index.js index 6240f4b..d321158 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -57,12 +57,10 @@ function processor(csn, options = {}) { function _getOpenApiForMultipleServices(csdl, csn, options) { let openApiDocs = {}; - for (let [content, metadata] of csdl) { - if (typeof content === "string") { - content = JSON.parse(content); - } - const openApiOptions = toOpenApiOptions(content, csn, options); - const openApiDocsForService = _getOpenApi(content, openApiOptions, metadata.file); + for (const [content, metadata] of csdl) { + const parsedContent = typeof content === "string" ? JSON.parse(content) : content; + const openApiOptions = toOpenApiOptions(parsedContent, csn, options); + const openApiDocsForService = _getOpenApi(parsedContent, openApiOptions, metadata.file); openApiDocs = { ...openApiDocs, ...openApiDocsForService }; } return openApiDocs; diff --git a/test/lib/compile/openapi.test.js b/test/lib/compile/openapi.test.js index 397aa59..812ec23 100644 --- a/test/lib/compile/openapi.test.js +++ b/test/lib/compile/openapi.test.js @@ -574,7 +574,7 @@ service CatalogService { assert.strictEqual(openAPI.paths["/F1"].get["x-sap-deprecated-operation"].notValidKey, undefined); }); - test('emits *:cds.compile.to.openapi events', async () => { + test('emits *:cds.compile.to.openapi events', () => { const csn = cds.compile.to.csn(` service CatalogService { entity Books { @@ -615,7 +615,7 @@ service CatalogService { } }); - test('allows modifying result in event handler', async () => { + test('allows modifying result in event handler', () => { const csn = cds.compile.to.csn(` service CatalogService { entity Books { @@ -647,7 +647,7 @@ service CatalogService { } }); - test('propagates errors from event handlers', async () => { + test('propagates errors from event handlers', () => { const csn = cds.compile.to.csn(` service CatalogService { entity Books { From 9c144f49df96da747dcddcd822626dd97ac44ae4 Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Tue, 2 Jun 2026 08:30:08 +0200 Subject: [PATCH 3/6] Parameter list to parameter objects --- lib/compile/csdl2openapi.js | 301 +++++++++++++++++++----------------- 1 file changed, 158 insertions(+), 143 deletions(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index 4dd7830..be055f9 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -434,7 +434,7 @@ module.exports.csdl2openapi = function ( const sourceName = (type && type[meta.voc.Common.Label]) || name; // entity sets and singletons are almost containment navigation properties child.$ContainsTarget = true; - pathItems(paths, `/${name}`, [], child, child, sourceName, sourceName, child, 0, ''); + pathItems({ paths, prefix: `/${name}`, prefixParameters: [], element: child, root: child, sourceName, targetName: sourceName, target: child, level: 0, navigationPath: '' }); } else if (child.$Action) { pathItemActionImport(paths, name, child); } else if (child.$Function) { @@ -449,18 +449,19 @@ module.exports.csdl2openapi = function ( /** * Add path and Path Item Object for a navigation segment - * @param {Paths} paths Paths Object to augment - * @param {string} prefix Prefix for path - * @param {Array} prefixParameters Parameter Objects for prefix - * @param {object} element Model element of navigation segment - * @param {object} root Root model element - * @param {string} sourceName Name of path source - * @param {string} targetName Name of path target - * @param {null | TargetRestrictions[]} target Target container child of path - * @param {number} level Number of navigation segments so far - * @param {string} navigationPath Path for finding navigation restrictions + * @param {object} options + * @param {Paths} options.paths Paths Object to augment + * @param {string} options.prefix Prefix for path + * @param {Array} options.prefixParameters Parameter Objects for prefix + * @param {object} options.element Model element of navigation segment + * @param {object} options.root Root model element + * @param {string} options.sourceName Name of path source + * @param {string} options.targetName Name of path target + * @param {null | TargetRestrictions[]} options.target Target container child of path + * @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) { + function pathItems({ paths, prefix, prefixParameters, element, root, sourceName, targetName, target, level, navigationPath }) { const name = prefix.substring(prefix.lastIndexOf('/') + 1); const type = meta.modelElement(element.$Type); const pathItem = {}; @@ -470,25 +471,25 @@ module.exports.csdl2openapi = function ( paths[prefix] = pathItem; if (prefixParameters.length > 0) pathItem.parameters = prefixParameters; - operationRead(pathItem, element, name, sourceName, targetName, target, level, restrictions, false, nonExpandable); + operationRead({ pathItem, element, name, sourceName, targetName, target, level, restrictions, byKey: false, nonExpandable }); if (!root['$cds.autoexpose'] && element.$Collection && (element.$ContainsTarget || level < 2 && target)) { - operationCreate(pathItem, element, name, sourceName, targetName, target, level, restrictions); + operationCreate({ pathItem, element, name, sourceName, targetName, target, level, restrictions }); } - pathItemsForBoundOperations(paths, prefix, prefixParameters, element, sourceName); + pathItemsForBoundOperations({ paths, prefix, prefixParameters, element, sourceName }); if (element.$ContainsTarget) { if (element.$Collection) { if (level < maxLevels) - pathItemsWithKey(paths, prefix, prefixParameters, element, root, sourceName, targetName, target, level, navigationPath, restrictions, nonExpandable); + pathItemsWithKey({ paths, prefix, prefixParameters, element, root, sourceName, targetName, target, level, navigationPath, restrictions, nonExpandable }); } else { if (!root['$cds.autoexpose']) { - operationUpdate(pathItem, element, name, sourceName, target, level, restrictions); + operationUpdate({ pathItem, element, name, sourceName, target, level, restrictions }); if (element.$Nullable) { - operationDelete(pathItem, element, name, sourceName, target, level, restrictions); + operationDelete({ pathItem, element, name, sourceName, target, level, restrictions }); } } - pathItemsForBoundOperations(paths, prefix, prefixParameters, element, sourceName); - pathItemsWithNavigation(paths, prefix, prefixParameters, type, root, sourceName, level, navigationPath); + pathItemsForBoundOperations({ paths, prefix, prefixParameters, element, sourceName }); + pathItemsWithNavigation({ paths, prefix, prefixParameters, type, root, sourceName, level, navigationPrefix: navigationPath }); } } @@ -529,20 +530,21 @@ module.exports.csdl2openapi = function ( /** * Add path and Path Item Object for a navigation segment with key - * @param {Paths} paths Paths Object to augment - * @param {string} prefix Prefix for path - * @param {Array} prefixParameters Parameter Objects for prefix - * @param {object} element Model element of navigation segment - * @param {object} root Root model element - * @param {string} sourceName Name of path source - * @param {string} targetName Name of path target - * @param {null | object} target Target container child of path - * @param {number} level Number of navigation segments so far - * @param {string} navigationPath Path for finding navigation restrictions - * @param {object} restrictions Navigation property restrictions of navigation segment - * @param {array} nonExpandable Non-expandable navigation properties + * @param {object} options + * @param {Paths} options.paths Paths Object to augment + * @param {string} options.prefix Prefix for path + * @param {Array} options.prefixParameters Parameter Objects for prefix + * @param {object} options.element Model element of navigation segment + * @param {object} options.root Root model element + * @param {string} options.sourceName Name of path source + * @param {string} options.targetName Name of path target + * @param {null | object} options.target Target container child of path + * @param {number} options.level Number of navigation segments so far + * @param {string} options.navigationPath Path for finding navigation restrictions + * @param {object} options.restrictions Navigation property restrictions of navigation segment + * @param {array} options.nonExpandable Non-expandable navigation properties */ - function pathItemsWithKey(paths, prefix, prefixParameters, element, root, sourceName, targetName, target, level, navigationPath, restrictions, nonExpandable) { + function pathItemsWithKey({ paths, prefix, prefixParameters, element, root, sourceName, targetName, target, level, navigationPath, restrictions, nonExpandable }) { const targetIndexable = target == null || target[meta.voc.Capabilities.IndexableByKey] != false; if (restrictions.IndexableByKey == true || restrictions.IndexableByKey != false && targetIndexable) { const name = prefix.substring(prefix.lastIndexOf('/') + 1); @@ -554,14 +556,14 @@ module.exports.csdl2openapi = function ( const pathItem = { parameters }; paths[path] = pathItem; - operationRead(pathItem, element, name, sourceName, targetName, target, level, restrictions, true, nonExpandable); + operationRead({ pathItem, element, name, sourceName, targetName, target, level, restrictions, byKey: true, nonExpandable }); if (!root['$cds.autoexpose']) { - operationUpdate(pathItem, element, name, sourceName, target, level, restrictions, true); - operationDelete(pathItem, element, name, sourceName, target, level, restrictions, true); + operationUpdate({ pathItem, element, name, sourceName, target, level, restrictions, byKey: true }); + operationDelete({ pathItem, element, name, sourceName, target, level, restrictions, byKey: true }); } - pathItemsForBoundOperations(paths, path, parameters, element, sourceName, true); - pathItemsWithNavigation(paths, path, parameters, type, root, sourceName, level, navigationPath); + pathItemsForBoundOperations({ paths, prefix: path, prefixParameters: parameters, element, sourceName, byKey: true }); + pathItemsWithNavigation({ paths, prefix: path, prefixParameters: parameters, type, root, sourceName, level, navigationPrefix: navigationPath }); if (Object.keys(pathItem).filter((i) => i !== "parameters").length === 0) delete paths[path]; @@ -571,23 +573,24 @@ module.exports.csdl2openapi = function ( /** * Construct Operation Object for create - * @param {object} pathItem Path Item Object to augment - * @param {object} element Model element of navigation segment - * @param {string} name Name of navigation segment - * @param {string} sourceName Name of path source - * @param {string} targetName Name of path target - * @param {null | TargetRestrictions[]} target Target container child of path - * @param {number} level Number of navigation segments so far - * @param {object} restrictions Navigation property restrictions of navigation segment + * @param {object} options + * @param {object} options.pathItem Path Item Object to augment + * @param {object} options.element Model element of navigation segment + * @param {string} options.name Name of navigation segment + * @param {string} options.sourceName Name of path source + * @param {string} options.targetName Name of path target + * @param {null | TargetRestrictions[]} options.target Target container child of path + * @param {number} options.level Number of navigation segments so far + * @param {object} options.restrictions Navigation property restrictions of navigation segment */ - function operationCreate(pathItem, element, name, sourceName, targetName, target, level, restrictions) { + function operationCreate({ pathItem, element, name, sourceName, targetName, target, level, restrictions }) { const insertRestrictions = restrictions.InsertRestrictions || target?.[meta.voc.Capabilities.InsertRestrictions] || {}; const countRestrictions = target?.[meta.voc.Capabilities.CountRestrictions]?.Countable === false // count property will be added if CountRestrictions is false if (insertRestrictions.Insertable !== false) { const lname = pluralize.singular(splitName(name)); const type = meta.modelElement(element.$Type); pathItem.post = { - summary: insertRestrictions.Description || operationSummary('Creates', name, sourceName, level, true, true), + summary: insertRestrictions.Description || operationSummary({ operation: 'Creates', name, sourceName, level, collection: true, byKey: true }), tags: [normaliseTag(sourceName)], requestBody: { description: type && type[meta.voc.Core.Description] || `New ${lname}`, @@ -598,7 +601,7 @@ module.exports.csdl2openapi = function ( } } }, - responses: response(201, `Created ${lname}`, { $Type: element.$Type }, insertRestrictions.ErrorResponses, !countRestrictions), + responses: response({ code: 201, description: `Created ${lname}`, type: { $Type: element.$Type }, errors: insertRestrictions.ErrorResponses, isCount: !countRestrictions }), }; if (insertRestrictions.LongDescription) pathItem.post.description = insertRestrictions.LongDescription; if (targetName && sourceName != targetName) pathItem.post.tags.push(normaliseTag(targetName)); @@ -608,15 +611,16 @@ module.exports.csdl2openapi = function ( /** * Construct operation summary - * @param {string} operation Operation (verb) - * @param {string} name Name of navigation segment - * @param {string} sourceName Name of path source - * @param {number} level Number of navigation segments so far - * @param {boolean} collection Access a collection - * @param {boolean} byKey Access by key + * @param {object} options + * @param {string} options.operation Operation (verb) + * @param {string} options.name Name of navigation segment + * @param {string} options.sourceName Name of path source + * @param {number} options.level Number of navigation segments so far + * @param {boolean} options.collection Access a collection + * @param {boolean} options.byKey Access by key * @return Operation Text */ - function operationSummary(operation, name, sourceName, level, collection, byKey) { + function operationSummary({ operation, name, sourceName, level, collection, byKey }) { const lname = splitName(name); const sname = splitName(sourceName); @@ -630,18 +634,19 @@ module.exports.csdl2openapi = function ( /** * Construct Operation Object for read - * @param {object} pathItem Path Item Object to augment - * @param {object} element Model element of navigation segment - * @param {string} name Name of navigation segment - * @param {string} sourceName Name of path source - * @param {string} targetName Name of path target - * @param {null | TargetRestrictions[]} target Target container child of path - * @param {number} level Number of navigation segments so far - * @param {object} restrictions Navigation property restrictions of navigation segment - * @param {boolean} byKey Read by key - * @param {array} nonExpandable Non-expandable navigation properties + * @param {object} options + * @param {object} options.pathItem Path Item Object to augment + * @param {object} options.element Model element of navigation segment + * @param {string} options.name Name of navigation segment + * @param {string} options.sourceName Name of path source + * @param {string} options.targetName Name of path target + * @param {null | TargetRestrictions[]} options.target Target container child of path + * @param {number} options.level Number of navigation segments so far + * @param {object} options.restrictions Navigation property restrictions of navigation segment + * @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) { + function operationRead({ pathItem, element, name, sourceName, targetName, target, level, restrictions, byKey, nonExpandable }) { const targetRestrictions = target?.[meta.voc.Capabilities.ReadRestrictions]; const readRestrictions = restrictions.ReadRestrictions || targetRestrictions || {}; const readByKeyRestrictions = readRestrictions.ReadByKeyRestrictions; @@ -658,11 +663,11 @@ module.exports.csdl2openapi = function ( const lname = splitName(name); const collection = !byKey && element.$Collection; const operation = { - summary: descriptions.Description || operationSummary('Retrieves', name, sourceName, level, element.$Collection, byKey), + summary: descriptions.Description || operationSummary({ operation: 'Retrieves', name, sourceName, level, collection: element.$Collection, byKey }), tags: [normaliseTag(sourceName)], parameters: [], - responses: response(200, `Retrieved ${byKey ? pluralize.singular(lname) : lname}`, { $Type: element.$Type, $Collection: collection }, - byKey ? readByKeyRestrictions?.ErrorResponses : readRestrictions?.ErrorResponses, !countRestrictions) + responses: response({ code: 200, description: `Retrieved ${byKey ? pluralize.singular(lname) : lname}`, type: { $Type: element.$Type, $Collection: collection }, + errors: byKey ? readByKeyRestrictions?.ErrorResponses : readRestrictions?.ErrorResponses, isCount: !countRestrictions }) }; const deltaSupported = element[meta.voc.Capabilities.ChangeTracking] && element[meta.voc.Capabilities.ChangeTracking].Supported; if (!byKey && deltaSupported) { @@ -1092,23 +1097,24 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Construct Operation Object for update - * @param {object} pathItem Path Item Object to augment - * @param {object} element Model element of navigation segment - * @param {string} name Name of navigation segment - * @param {string} sourceName Name of path source - * @param {null | TargetRestrictions[]} target Target container child of path - * @param {number} level Number of navigation segments so far - * @param {object} restrictions Navigation property restrictions of navigation segment - * @param {boolean} byKey Update by key + * @param {object} options + * @param {object} options.pathItem Path Item Object to augment + * @param {object} options.element Model element of navigation segment + * @param {string} options.name Name of navigation segment + * @param {string} options.sourceName Name of path source + * @param {null | TargetRestrictions[]} options.target Target container child of path + * @param {number} options.level Number of navigation segments so far + * @param {object} 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) { + function operationUpdate({ pathItem, element, name, sourceName, target, level, restrictions, byKey = false }) { 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]) { const type = meta.modelElement(element.$Type); const operation = { - summary: updateRestrictions.Description || operationSummary('Changes', name, sourceName, level, element.$Collection, byKey), + summary: updateRestrictions.Description || operationSummary({ operation: 'Changes', name, sourceName, level, collection: element.$Collection, byKey }), tags: [normaliseTag(sourceName)], requestBody: { description: type && type[meta.voc.Core.Description] || 'New property values', @@ -1119,7 +1125,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot } } }, - responses: response(204, "Success", undefined, updateRestrictions.ErrorResponses, !countRestrictions), + responses: response({ code: 204, description: "Success", type: undefined, errors: updateRestrictions.ErrorResponses, isCount: !countRestrictions }), }; if (updateRestrictions.LongDescription) operation.description = updateRestrictions.LongDescription; customParameters(operation, updateRestrictions); @@ -1130,23 +1136,24 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Construct Operation Object for delete - * @param {object} pathItem Path Item Object to augment - * @param {object} element Model element of navigation segment - * @param {string} name Name of navigation segment - * @param {string} sourceName Name of path source - * @param {null | TargetRestrictions[]} target Target container child of path - * @param {number} level Number of navigation segments so far - * @param {object} restrictions Navigation property restrictions of navigation segment - * @param {boolean} byKey Delete by key + * @param {object} options + * @param {object} options.pathItem Path Item Object to augment + * @param {object} options.element Model element of navigation segment + * @param {string} options.name Name of navigation segment + * @param {string} options.sourceName Name of path source + * @param {null | TargetRestrictions[]} options.target Target container child of path + * @param {number} options.level Number of navigation segments so far + * @param {object} options.restrictions Navigation property restrictions of navigation segment + * @param {boolean} [options.byKey=false] Delete by key */ - function operationDelete(pathItem, element, name, sourceName, target, level, restrictions, byKey = false) { + function operationDelete({ pathItem, element, name, sourceName, target, level, restrictions, byKey = false }) { const deleteRestrictions = restrictions.DeleteRestrictions || target?.[meta.voc.Capabilities.DeleteRestrictions] || {}; const countRestrictions = target?.[meta.voc.Capabilities.CountRestrictions]?.Countable === false if (deleteRestrictions.Deletable !== false) { pathItem.delete = { - summary: deleteRestrictions.Description || operationSummary('Deletes', name, sourceName, level, element.$Collection, byKey), + summary: deleteRestrictions.Description || operationSummary({ operation: 'Deletes', name, sourceName, level, collection: element.$Collection, byKey }), tags: [normaliseTag(sourceName)], - responses: response(204, "Success", undefined, deleteRestrictions.ErrorResponses, !countRestrictions), + responses: response({ code: 204, description: "Success", type: undefined, errors: deleteRestrictions.ErrorResponses, isCount: !countRestrictions }), }; if (deleteRestrictions.LongDescription) pathItem.delete.description = deleteRestrictions.LongDescription; customParameters(pathItem.delete, deleteRestrictions); @@ -1155,15 +1162,17 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Add paths and Path Item Objects for navigation segments - * @param {object} paths The Paths Object to augment - * @param {string} prefix Prefix for path - * @param {Array} prefixParameters Parameter Objects for prefix - * @param {object} type Entity type object of navigation segment - * @param {string} sourceName Name of path source - * @param {number} level Number of navigation segments so far - * @param {string} navigationPrefix Path for finding navigation restrictions + * @param {object} options + * @param {object} options.paths The Paths Object to augment + * @param {string} options.prefix Prefix for path + * @param {Array} options.prefixParameters Parameter Objects for prefix + * @param {object} options.type Entity type object of navigation segment + * @param {object} options.root Root element + * @param {string} options.sourceName Name of path source + * @param {number} options.level Number of navigation segments so far + * @param {string} options.navigationPrefix Path for finding navigation restrictions */ - function pathItemsWithNavigation(paths, prefix, prefixParameters, type, root, sourceName, level, navigationPrefix) { + function pathItemsWithNavigation({ paths, prefix, prefixParameters, type, root, sourceName, level, navigationPrefix }) { const navigationRestrictions = root[meta.voc.Capabilities.NavigationRestrictions] ?? {}; const rootNavigable = level == 0 && enumMember(navigationRestrictions.Navigability) != 'None' || level == 1 && enumMember(navigationRestrictions.Navigability) != 'Single' @@ -1183,7 +1192,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot const target = entityContainer[targetSetName]; const targetType = target && meta.modelElement(target.$Type); const targetName = (targetType && targetType[meta.voc.Common.Label]) || targetSetName; - pathItems(paths, `${prefix}/${name}`, prefixParameters, properties[name], root, sourceName, targetName, target, level + 1, navigationPath); + pathItems({ paths, prefix: `${prefix}/${name}`, prefixParameters, element: properties[name], root, sourceName, targetName, target, level: level + 1, navigationPath }); } }); } @@ -1331,14 +1340,15 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Add path and Path Item Object for actions and functions bound to the element - * @param {Paths} paths Paths Object to augment - * @param {string} prefix Prefix for path - * @param {Array} prefixParameters Parameter Objects for prefix - * @param {object} element Model element the operations are bound to - * @param {string} sourceName Name of path source - * @param {boolean} byKey read by key + * @param {object} options + * @param {Paths} options.paths Paths Object to augment + * @param {string} options.prefix Prefix for path + * @param {Array} options.prefixParameters Parameter Objects for prefix + * @param {object} options.element Model element the operations are bound to + * @param {string} options.sourceName Name of path source + * @param {boolean} [options.byKey=false] Read by key */ - function pathItemsForBoundOperations(paths, prefix, prefixParameters, element, sourceName, byKey = false) { + function pathItemsForBoundOperations({ paths, prefix, prefixParameters, element, sourceName, byKey = false }) { //ignore operations on navigation path if (element.$Kind === "NavigationProperty") { return; @@ -1347,9 +1357,9 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot overloads.forEach(item => { const pathName = shortActionPaths && item.name.includes('.') ? nameParts(item.name).name : item.name; if (item.overload.$Kind == 'Action') - pathItemAction(paths, `${prefix}/${pathName}`, prefixParameters, item.name, item.overload, sourceName); + pathItemAction({ paths, prefix: `${prefix}/${pathName}`, prefixParameters, actionName: item.name, overload: item.overload, sourceName }); else - pathItemFunction(paths, `${prefix}/${pathName}`, prefixParameters, item.name, item.overload, sourceName); + pathItemFunction({ paths, prefix: `${prefix}/${pathName}`, prefixParameters, functionName: item.name, overload: item.overload, sourceName }); }); } @@ -1361,27 +1371,28 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot */ function pathItemActionImport(paths, name, child) { const overload = meta.modelElement(child.$Action).find(pOverload => !pOverload.$IsBound); - pathItemAction(paths, `/${name}`, [], child.$Action, overload, child.$EntitySet, child); + pathItemAction({ paths, prefix: `/${name}`, prefixParameters: [], actionName: child.$Action, overload, sourceName: child.$EntitySet, actionImport: child }); } /** * Add path and Path Item Object for action overload - * @param {Paths} paths Paths Object to augment - * @param {string} prefix Prefix for path - * @param {Array} prefixParameters Parameter Objects for prefix - * @param {string} actionName Qualified name of function - * @param {object} overload Function overload - * @param {string} sourceName Name of path source - * @param {object} actionImport Action import + * @param {object} options + * @param {Paths} options.paths Paths Object to augment + * @param {string} options.prefix Prefix for path + * @param {Array} options.prefixParameters Parameter Objects for prefix + * @param {string} options.actionName Qualified name of function + * @param {object} options.overload Function overload + * @param {string} options.sourceName Name of path source + * @param {object} [options.actionImport={}] Action import */ - function pathItemAction(paths, prefix, prefixParameters, actionName, overload, sourceName, actionImport = {}) { + function pathItemAction({ paths, prefix, prefixParameters, actionName, overload, sourceName, actionImport = {} }) { const name = actionName.indexOf('.') === -1 ? actionName : nameParts(actionName).name; const pathItem = { post: { summary: actionImport[meta.voc.Core.Description] || overload[meta.voc.Core.Description] || `Invokes action ${name}`, tags: [normaliseTag(overload[meta.voc.Common.Label] || sourceName || 'Service Operations')], - responses: overload.$ReturnType ? response(200, "Success", overload.$ReturnType, overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses) - : response(204, "Success", undefined, overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses), + responses: overload.$ReturnType ? response({ code: 200, description: "Success", type: overload.$ReturnType, errors: overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses }) + : response({ code: 204, description: "Success", type: undefined, errors: overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses }), } }; const actionExtension = getExtensions(overload, 'operation'); @@ -1433,20 +1444,21 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot function pathItemFunctionImport(paths, name, child) { const overloads = meta.modelElement(child.$Function); console.assert(overloads, `Unknown function "${child.$Function}" in function import "${name}"`); - overloads && overloads.filter(overload => !overload.$IsBound).forEach(overload => pathItemFunction(paths, `/${name}`, [], child.$Function, overload, child.$EntitySet, child)); + overloads && overloads.filter(overload => !overload.$IsBound).forEach(overload => pathItemFunction({ paths, prefix: `/${name}`, prefixParameters: [], functionName: child.$Function, overload, sourceName: child.$EntitySet, functionImport: child })); } /** * Add path and Path Item Object for function overload - * @param {Paths} paths Paths Object to augment - * @param {string} prefix Prefix for path - * @param {Array} prefixParameters Parameter Objects for prefix - * @param {string} functionName Qualified name of function - * @param {object} overload Function overload - * @param {string} sourceName Name of path source - * @param {object} functionImport Function Import + * @param {object} options + * @param {Paths} options.paths Paths Object to augment + * @param {string} options.prefix Prefix for path + * @param {Array} options.prefixParameters Parameter Objects for prefix + * @param {string} options.functionName Qualified name of function + * @param {object} options.overload Function overload + * @param {string} options.sourceName Name of path source + * @param {object} [options.functionImport={}] Function Import */ - function pathItemFunction(paths, prefix, prefixParameters, functionName, overload, sourceName, functionImport = {}) { + function pathItemFunction({ paths, prefix, prefixParameters, functionName, overload, sourceName, functionImport = {} }) { const name = functionName.indexOf('.') === -1 ? functionName : nameParts(functionName).name; let parameters = overload.$Parameter || []; if (overload.$IsBound) parameters = parameters.slice(1); @@ -1521,7 +1533,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot summary: functionImport[meta.voc.Core.Description] || overload[meta.voc.Core.Description] || `Invokes function ${name}`, tags: [normaliseTag(overload[meta.voc.Common.Label] || sourceName || 'Service Operations')], parameters: prefixParameters.concat(params), - responses: response(200, "Success", overload.$ReturnType, overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses), + responses: response({ code: 200, description: "Success", type: overload.$ReturnType, errors: overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses }), } }; const functionExtension = getExtensions(overload, 'operation'); @@ -1597,13 +1609,15 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Construct Responses Object - * @param {string | number} code HTTP response code - * @param {string} description Description - * @param {object} type Response type object - * @param {array} errors Array of operation-specific status codes with descriptions + * @param {object} options + * @param {string | number} options.code HTTP response code + * @param {string} options.description Description + * @param {object} options.type Response type object + * @param {array} options.errors Array of operation-specific status codes with descriptions + * @param {boolean} [options.isCount=true] * @returns {Record} */ - function response(code, description, type, errors, isCount = true) { + function response({ code, description, type, errors, isCount = true }) { /** @type {Record} */ const r = { [code]: { @@ -1708,7 +1722,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot switch (type.$Kind) { case "ComplexType": case "EntityType": - schemasForStructuredType(unordered, r.namespace, r.name, type, r.suffix); + schemasForStructuredType({ schemas: unordered, qualifier: r.namespace, name: r.name, type, suffix: r.suffix }); break; case "EnumType": schemaForEnumerationType(unordered, r.namespace, r.name, type); @@ -1820,14 +1834,15 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot /** * Construct Schema Objects for a structured type - * @param {object} schemas Map of Schema Objects to augment - * @param {string} qualifier Qualifier for structured type - * @param {string} name Simple name of structured type - * @param {string} suffix Suffix for read/create/update - * @param {object} type Structured type + * @param {object} options + * @param {object} options.schemas Map of Schema Objects to augment + * @param {string} options.qualifier Qualifier for structured type + * @param {string} options.name Simple name of structured type + * @param {object} options.type Structured type + * @param {string} options.suffix Suffix for read/create/update * @return {object} Map of Schemas Objects */ - function schemasForStructuredType(schemas, qualifier, name, type, suffix) { + function schemasForStructuredType({ schemas, qualifier, name, type, suffix }) { const schemaName = `${qualifier}.${name}${suffix}`; const baseName = `${qualifier}.${name}`; const isKey = keyMap(type); From 12386b8f5994686bee974a625f46a5584faff03b Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Tue, 2 Jun 2026 08:39:17 +0200 Subject: [PATCH 4/6] Break up long lines --- lib/compile/csdl2openapi.js | 247 ++++++++++++++++++++++++++++++++---- 1 file changed, 221 insertions(+), 26 deletions(-) diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index be055f9..cc592b7 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -434,7 +434,18 @@ module.exports.csdl2openapi = function ( const sourceName = (type && type[meta.voc.Common.Label]) || name; // entity sets and singletons are almost containment navigation properties child.$ContainsTarget = true; - pathItems({ paths, prefix: `/${name}`, prefixParameters: [], element: child, root: child, sourceName, targetName: sourceName, target: child, level: 0, navigationPath: '' }); + pathItems({ + paths, + prefix: `/${name}`, + prefixParameters: [], + element: child, + root: child, + sourceName, + targetName: sourceName, + target: child, + level: 0, + navigationPath: '' + }); } else if (child.$Action) { pathItemActionImport(paths, name, child); } else if (child.$Function) { @@ -471,7 +482,18 @@ module.exports.csdl2openapi = function ( paths[prefix] = pathItem; if (prefixParameters.length > 0) pathItem.parameters = prefixParameters; - operationRead({ pathItem, element, name, sourceName, targetName, target, level, restrictions, byKey: false, nonExpandable }); + operationRead({ + pathItem, + element, + name, + sourceName, + targetName, + target, + level, + restrictions, + byKey: false, + nonExpandable + }); if (!root['$cds.autoexpose'] && element.$Collection && (element.$ContainsTarget || level < 2 && target)) { operationCreate({ pathItem, element, name, sourceName, targetName, target, level, restrictions }); } @@ -480,7 +502,20 @@ module.exports.csdl2openapi = function ( if (element.$ContainsTarget) { if (element.$Collection) { if (level < maxLevels) - pathItemsWithKey({ paths, prefix, prefixParameters, element, root, sourceName, targetName, target, level, navigationPath, restrictions, nonExpandable }); + pathItemsWithKey({ + paths, + prefix, + prefixParameters, + element, + root, + sourceName, + targetName, + target, + level, + navigationPath, + restrictions, + nonExpandable + }); } else { if (!root['$cds.autoexpose']) { operationUpdate({ pathItem, element, name, sourceName, target, level, restrictions }); @@ -489,7 +524,16 @@ module.exports.csdl2openapi = function ( } } pathItemsForBoundOperations({ paths, prefix, prefixParameters, element, sourceName }); - pathItemsWithNavigation({ paths, prefix, prefixParameters, type, root, sourceName, level, navigationPrefix: navigationPath }); + pathItemsWithNavigation({ + paths, + prefix, + prefixParameters, + type, + root, + sourceName, + level, + navigationPrefix: navigationPath + }); } } @@ -556,14 +600,59 @@ module.exports.csdl2openapi = function ( const pathItem = { parameters }; paths[path] = pathItem; - operationRead({ pathItem, element, name, sourceName, targetName, target, level, restrictions, byKey: true, nonExpandable }); + operationRead({ + pathItem, + element, + name, + sourceName, + targetName, + target, + level, + restrictions, + byKey: true, + nonExpandable + }); if (!root['$cds.autoexpose']) { - operationUpdate({ pathItem, element, name, sourceName, target, level, restrictions, byKey: true }); - operationDelete({ pathItem, element, name, sourceName, target, level, restrictions, byKey: true }); + operationUpdate({ + pathItem, + element, + name, + sourceName, + target, + level, + restrictions, + byKey: true + }); + operationDelete({ + pathItem, + element, + name, + sourceName, + target, + level, + restrictions, + byKey: true + }); } - pathItemsForBoundOperations({ paths, prefix: path, prefixParameters: parameters, element, sourceName, byKey: true }); - pathItemsWithNavigation({ paths, prefix: path, prefixParameters: parameters, type, root, sourceName, level, navigationPrefix: navigationPath }); + pathItemsForBoundOperations({ + paths, + prefix: path, + prefixParameters: parameters, + element, + sourceName, + byKey: true + }); + pathItemsWithNavigation({ + paths, + prefix: path, + prefixParameters: parameters, + type, + root, + sourceName, + level, + navigationPrefix: navigationPath + }); if (Object.keys(pathItem).filter((i) => i !== "parameters").length === 0) delete paths[path]; @@ -590,7 +679,14 @@ module.exports.csdl2openapi = function ( const lname = pluralize.singular(splitName(name)); const type = meta.modelElement(element.$Type); pathItem.post = { - summary: insertRestrictions.Description || operationSummary({ operation: 'Creates', name, sourceName, level, collection: true, byKey: true }), + summary: insertRestrictions.Description || operationSummary({ + operation: 'Creates', + name, + sourceName, + level, + collection: true, + byKey: true + }), tags: [normaliseTag(sourceName)], requestBody: { description: type && type[meta.voc.Core.Description] || `New ${lname}`, @@ -601,7 +697,13 @@ module.exports.csdl2openapi = function ( } } }, - responses: response({ code: 201, description: `Created ${lname}`, type: { $Type: element.$Type }, errors: insertRestrictions.ErrorResponses, isCount: !countRestrictions }), + responses: response({ + code: 201, + description: `Created ${lname}`, + type: { $Type: element.$Type }, + errors: insertRestrictions.ErrorResponses, + isCount: !countRestrictions + }), }; if (insertRestrictions.LongDescription) pathItem.post.description = insertRestrictions.LongDescription; if (targetName && sourceName != targetName) pathItem.post.tags.push(normaliseTag(targetName)); @@ -663,11 +765,23 @@ module.exports.csdl2openapi = function ( const lname = splitName(name); const collection = !byKey && element.$Collection; const operation = { - summary: descriptions.Description || operationSummary({ operation: 'Retrieves', name, sourceName, level, collection: element.$Collection, byKey }), + summary: descriptions.Description || operationSummary({ + operation: 'Retrieves', + name, + sourceName, + level, + collection: element.$Collection, + byKey + }), tags: [normaliseTag(sourceName)], parameters: [], - responses: response({ code: 200, description: `Retrieved ${byKey ? pluralize.singular(lname) : lname}`, type: { $Type: element.$Type, $Collection: collection }, - errors: byKey ? readByKeyRestrictions?.ErrorResponses : readRestrictions?.ErrorResponses, isCount: !countRestrictions }) + responses: response({ + code: 200, + description: `Retrieved ${byKey ? pluralize.singular(lname) : lname}`, + type: { $Type: element.$Type, $Collection: collection }, + errors: byKey ? readByKeyRestrictions?.ErrorResponses : readRestrictions?.ErrorResponses, + isCount: !countRestrictions + }) }; const deltaSupported = element[meta.voc.Capabilities.ChangeTracking] && element[meta.voc.Capabilities.ChangeTracking].Supported; if (!byKey && deltaSupported) { @@ -1114,7 +1228,14 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot const type = meta.modelElement(element.$Type); const operation = { - summary: updateRestrictions.Description || operationSummary({ operation: 'Changes', name, sourceName, level, collection: element.$Collection, byKey }), + summary: updateRestrictions.Description || operationSummary({ + operation: 'Changes', + name, + sourceName, + level, + collection: element.$Collection, + byKey + }), tags: [normaliseTag(sourceName)], requestBody: { description: type && type[meta.voc.Core.Description] || 'New property values', @@ -1125,7 +1246,13 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot } } }, - responses: response({ code: 204, description: "Success", type: undefined, errors: updateRestrictions.ErrorResponses, isCount: !countRestrictions }), + responses: response({ + code: 204, + description: "Success", + type: undefined, + errors: updateRestrictions.ErrorResponses, + isCount: !countRestrictions + }), }; if (updateRestrictions.LongDescription) operation.description = updateRestrictions.LongDescription; customParameters(operation, updateRestrictions); @@ -1151,9 +1278,22 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot const countRestrictions = target?.[meta.voc.Capabilities.CountRestrictions]?.Countable === false if (deleteRestrictions.Deletable !== false) { pathItem.delete = { - summary: deleteRestrictions.Description || operationSummary({ operation: 'Deletes', name, sourceName, level, collection: element.$Collection, byKey }), + summary: deleteRestrictions.Description || operationSummary({ + operation: 'Deletes', + name, + sourceName, + level, + collection: element.$Collection, + byKey + }), tags: [normaliseTag(sourceName)], - responses: response({ code: 204, description: "Success", type: undefined, errors: deleteRestrictions.ErrorResponses, isCount: !countRestrictions }), + responses: response({ + code: 204, + description: "Success", + type: undefined, + errors: deleteRestrictions.ErrorResponses, + isCount: !countRestrictions + }), }; if (deleteRestrictions.LongDescription) pathItem.delete.description = deleteRestrictions.LongDescription; customParameters(pathItem.delete, deleteRestrictions); @@ -1192,7 +1332,18 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot const target = entityContainer[targetSetName]; const targetType = target && meta.modelElement(target.$Type); const targetName = (targetType && targetType[meta.voc.Common.Label]) || targetSetName; - pathItems({ paths, prefix: `${prefix}/${name}`, prefixParameters, element: properties[name], root, sourceName, targetName, target, level: level + 1, navigationPath }); + pathItems({ + paths, + prefix: `${prefix}/${name}`, + prefixParameters, + element: properties[name], + root, + sourceName, + targetName, + target, + level: level + 1, + navigationPath + }); } }); } @@ -1357,9 +1508,23 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot overloads.forEach(item => { const pathName = shortActionPaths && item.name.includes('.') ? nameParts(item.name).name : item.name; if (item.overload.$Kind == 'Action') - pathItemAction({ paths, prefix: `${prefix}/${pathName}`, prefixParameters, actionName: item.name, overload: item.overload, sourceName }); + pathItemAction({ + paths, + prefix: `${prefix}/${pathName}`, + prefixParameters, + actionName: item.name, + overload: item.overload, + sourceName + }); else - pathItemFunction({ paths, prefix: `${prefix}/${pathName}`, prefixParameters, functionName: item.name, overload: item.overload, sourceName }); + pathItemFunction({ + paths, + prefix: `${prefix}/${pathName}`, + prefixParameters, + functionName: item.name, + overload: item.overload, + sourceName + }); }); } @@ -1371,7 +1536,15 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot */ function pathItemActionImport(paths, name, child) { const overload = meta.modelElement(child.$Action).find(pOverload => !pOverload.$IsBound); - pathItemAction({ paths, prefix: `/${name}`, prefixParameters: [], actionName: child.$Action, overload, sourceName: child.$EntitySet, actionImport: child }); + pathItemAction({ + paths, + prefix: `/${name}`, + prefixParameters: [], + actionName: child.$Action, + overload, + sourceName: child.$EntitySet, + actionImport: child + }); } /** @@ -1391,8 +1564,17 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot post: { summary: actionImport[meta.voc.Core.Description] || overload[meta.voc.Core.Description] || `Invokes action ${name}`, tags: [normaliseTag(overload[meta.voc.Common.Label] || sourceName || 'Service Operations')], - responses: overload.$ReturnType ? response({ code: 200, description: "Success", type: overload.$ReturnType, errors: overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses }) - : response({ code: 204, description: "Success", type: undefined, errors: overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses }), + responses: overload.$ReturnType ? response({ + code: 200, + description: "Success", + type: overload.$ReturnType, + errors: overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses + }) : response({ + code: 204, + description: "Success", + type: undefined, + errors: overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses + }), } }; const actionExtension = getExtensions(overload, 'operation'); @@ -1444,7 +1626,15 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot function pathItemFunctionImport(paths, name, child) { const overloads = meta.modelElement(child.$Function); console.assert(overloads, `Unknown function "${child.$Function}" in function import "${name}"`); - overloads && overloads.filter(overload => !overload.$IsBound).forEach(overload => pathItemFunction({ paths, prefix: `/${name}`, prefixParameters: [], functionName: child.$Function, overload, sourceName: child.$EntitySet, functionImport: child })); + overloads && overloads.filter(overload => !overload.$IsBound).forEach(overload => pathItemFunction({ + paths, + prefix: `/${name}`, + prefixParameters: [], + functionName: child.$Function, + overload, + sourceName: child.$EntitySet, + functionImport: child + })); } /** @@ -1533,7 +1723,12 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot summary: functionImport[meta.voc.Core.Description] || overload[meta.voc.Core.Description] || `Invokes function ${name}`, tags: [normaliseTag(overload[meta.voc.Common.Label] || sourceName || 'Service Operations')], parameters: prefixParameters.concat(params), - responses: response({ code: 200, description: "Success", type: overload.$ReturnType, errors: overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses }), + responses: response({ + code: 200, + description: "Success", + type: overload.$ReturnType, + errors: overload[meta.voc.Capabilities.OperationRestrictions]?.ErrorResponses + }), } }; const functionExtension = getExtensions(overload, 'operation'); From 856c7d47643a657a70933d4063dc78e4b149280b Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Tue, 2 Jun 2026 09:32:16 +0200 Subject: [PATCH 5/6] Fallback for undefined properties --- lib/compile/csdl.js | 4 ++-- lib/compile/csdl2openapi.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compile/csdl.js b/lib/compile/csdl.js index b1a7896..ae0b5eb 100644 --- a/lib/compile/csdl.js +++ b/lib/compile/csdl.js @@ -78,8 +78,8 @@ class CSDLMeta { this.#getVocabularies(this.alias); Object.keys(this.csdl).filter(name => isIdentifier(name)).forEach(name => { - const schema = this.csdl[name]; - const qualifier = schema.$Alias || name; + const schema = this.csdl[name] ?? {}; + const qualifier = schema.$Alias ?? name; const isDefaultNamespace = schema[this.voc.Core.DefaultNamespace]; this.alias[name] = qualifier; diff --git a/lib/compile/csdl2openapi.js b/lib/compile/csdl2openapi.js index cc592b7..f97b57b 100644 --- a/lib/compile/csdl2openapi.js +++ b/lib/compile/csdl2openapi.js @@ -1930,7 +1930,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot // Add @OpenAPI.Extensions at entity level to schema object Object.keys(csdl).filter(name => isIdentifier(name)).forEach(namespace => { - const schema = csdl[namespace]; + const schema = csdl[namespace] ?? {}; Object.keys(schema).filter(name => isIdentifier(name)).forEach(name => { const type = schema[name]; if (type.$Kind === 'EntityType' || type.$Kind === 'ComplexType') { From 9fcde8f8ce22ebe8555d81d55ade58b89a7539ae Mon Sep 17 00:00:00 2001 From: Daniel O'Grady Date: Tue, 2 Jun 2026 09:33:36 +0200 Subject: [PATCH 6/6] Fallback --- lib/compile/diagram.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compile/diagram.js b/lib/compile/diagram.js index 98497ab..48c9cfc 100644 --- a/lib/compile/diagram.js +++ b/lib/compile/diagram.js @@ -34,7 +34,7 @@ class Diagram { this.#comma = ''; Object.keys(this.#meta.csdl).filter(name => isIdentifier(name)).forEach(namespace => { - const schema = this.#meta.csdl[namespace]; + const schema = this.#meta.csdl[namespace] ?? {}; Object.keys(schema) .filter(name => isIdentifier(name) && ['EntityType', 'ComplexType'].includes(schema[name].$Kind)) .forEach(typeName => {