@sap/cds-dk 7.1.0 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/build.js +3 -3
- package/bin/cds.js +2 -1
- package/bin/compile/index.js +5 -1
- package/bin/gen.js +27 -0
- package/bin/help.js +2 -1
- package/bin/watch.js +3 -4
- package/bin/watched.js +2 -3
- package/lib/bind/bindingUtil.js +1 -0
- package/lib/build/buildTaskEngine.js +13 -17
- package/lib/build/buildTaskFactory.js +23 -27
- package/lib/build/buildTaskHandler.js +6 -12
- package/lib/build/buildTaskProviderFactory.js +9 -12
- package/lib/build/constants.js +2 -1
- package/lib/build/index.js +1 -1
- package/lib/build/provider/buildTaskHandlerInternal.js +2 -2
- package/lib/build/provider/buildTaskProviderInternal.js +15 -25
- package/lib/build/provider/fiori/index.js +4 -3
- package/lib/build/provider/hana/index.js +14 -7
- package/lib/build/provider/java/index.js +2 -2
- package/lib/build/provider/nodejs/index.js +51 -18
- package/lib/build/util.js +11 -19
- package/lib/compile/openapi/csdl2openapi.js +30 -21
- package/lib/compile/openapi/index.js +7 -3
- package/lib/import/common.js +1 -1
- package/lib/import/message.js +6 -1
- package/lib/import/odata/metadataconverter/MetadataConverter.js +13 -9
- package/lib/import/odata/v4parser.js +26 -12
- package/lib/init/constants.js +6 -11
- package/lib/init/index.js +84 -71
- package/lib/init/template/_merging/registry-mta.js +0 -12
- package/lib/init/template/approuter/files/mta.yaml.hbs +1 -1
- package/lib/init/template/approuter/index.js +7 -6
- package/lib/init/template/cf-manifest/files/services-manifest.yml.hbs +0 -5
- package/lib/init/template/cf-manifest/index.js +2 -2
- package/lib/init/template/h2/index.js +16 -0
- package/lib/init/template/hana/index.js +2 -3
- package/lib/init/template/helm/chart/values.root.schema.json +0 -5
- package/lib/init/template/helm/chart/values.schema.json +0 -4
- package/lib/init/template/helm/index.js +1 -1
- package/lib/init/template/lint/index.js +3 -7
- package/lib/init/template/multitenancy/files/package.json.hbs +0 -3
- package/lib/init/template/multitenancy/index.js +2 -0
- package/lib/init/template/nodejs/index.js +3 -3
- package/lib/init/template/pipeline/index.js +2 -4
- package/lib/init/template/sample/index.js +5 -7
- package/lib/init/template/sqlite/index.js +8 -3
- package/lib/init/template/tiny-sample/index.js +16 -30
- package/lib/init/util/mvn.js +25 -1
- package/lib/init/util/projectReader.js +3 -7
- package/lib/init/util/template.js +7 -6
- package/lib/watch/node-watch.js +6 -1
- package/npm-shrinkwrap.json +46 -84
- package/package.json +5 -4
- package/lib/init/template/auditlog/files/mta.yaml.hbs +0 -14
- package/lib/init/template/auditlog/files/package.json.hbs +0 -27
- package/lib/init/template/auditlog/index.js +0 -74
|
@@ -2,7 +2,7 @@ const fs = require('fs')
|
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const cds = require('../../../cds')
|
|
4
4
|
const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
|
|
5
|
-
const { OUTPUT_MODE, OUTPUT_MODE_RESULT, FILE_EXT_CDS, CONTENT_LANGUAGE_BUNDLES, CONTENT_DEFAULT_CSN, DEFAULT_CSN_FILE_NAME } = require('../../constants')
|
|
5
|
+
const { OUTPUT_MODE, OUTPUT_MODE_RESULT, FILE_EXT_CDS, CONTENT_LANGUAGE_BUNDLES, CONTENT_DEFAULT_CSN, DEFAULT_CSN_FILE_NAME, OUTPUT_MODE_FILESYSTEM } = require('../../constants')
|
|
6
6
|
const { getI18nDefaultFolder } = require('../../util')
|
|
7
7
|
const DEFAULT_COMPILE_DEST_FOLDER = path.normalize('src/main/resources/edmx')
|
|
8
8
|
|
|
@@ -34,7 +34,7 @@ class JavaModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
34
34
|
this._result.languageBundles = i18n.bundles
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
if (this.isStagingBuild() && this.hasBuildOption(OUTPUT_MODE,
|
|
37
|
+
if (this.isStagingBuild() && this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM)) {
|
|
38
38
|
await this._copyNativeContent(src, dest)
|
|
39
39
|
}
|
|
40
40
|
return this._result
|
|
@@ -52,31 +52,64 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
52
52
|
if (this.isStagingBuild() && this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_FILESYSTEM)) {
|
|
53
53
|
const srcSrv = this.task.src === cds.root ? path.resolve(this.task.src, cds.env.folders.srv) : this.task.src
|
|
54
54
|
await this._copyNativeContent(cds.root, srcSrv, destRoot, destSrv)
|
|
55
|
-
|
|
55
|
+
if (this.context.options.workspaces) {
|
|
56
|
+
await this._addWorkspaceDependencies(destRoot)
|
|
57
|
+
}
|
|
56
58
|
}
|
|
57
59
|
return this._result
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
// REVISIT: fix for
|
|
61
|
-
// Instead of adding relative paths we may better delete the entire entry as relative paths
|
|
62
|
-
// cannot be resolved in cloud deployments anyway.
|
|
62
|
+
// REVISIT: fix for simple mono repos using npm workspaces
|
|
63
63
|
// Support for HANA native artifacts defined in sub-modules required
|
|
64
|
-
async
|
|
65
|
-
const packageJson = path.join(dest, 'package.json')
|
|
64
|
+
async _addWorkspaceDependencies(dest) {
|
|
66
65
|
try {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
for (let dependency in dependencies) {
|
|
71
|
-
if (dependency.startsWith('@capire/')) {
|
|
72
|
-
dependencies[dependency] = path.relative(dest, path.dirname(require.resolve(dependency + '/package.json')))
|
|
73
|
-
changed = true
|
|
74
|
-
}
|
|
66
|
+
const workspaceRoot = path.resolve(cds.root, this.getBuildOption("workspaceRoot") ?? '.')
|
|
67
|
+
if (workspaceRoot !== cds.root && !fs.existsSync(workspaceRoot)) {
|
|
68
|
+
throw new BuildError(`The given workspaceRoot path ${workspaceRoot} does not exist`)
|
|
75
69
|
}
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
const packageJson = require(path.join(dest, 'package.json'))
|
|
71
|
+
const workspaces = workspaceRoot === cds.root ? packageJson.workspaces : require(path.join(workspaceRoot, 'package.json')).workspaces
|
|
72
|
+
if (workspaces?.length) {
|
|
73
|
+
this.logger._debug && this.logger.debug(`Existing npm workspace configuration '${JSON.stringify(workspaces, ' ')}'`)
|
|
74
|
+
const { tar } = require('../../../../lib').utils
|
|
75
|
+
const { dependencies } = packageJson
|
|
76
|
+
const folders = fs.readdirSync(workspaceRoot).filter(res => {
|
|
77
|
+
try {
|
|
78
|
+
return fs.lstatSync(path.join(workspaceRoot, res)).isDirectory()
|
|
79
|
+
} catch (e) {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
this.logger._debug && this.logger.debug(`Existing workspace subfolders '${JSON.stringify(folders, ' ')}'`)
|
|
84
|
+
|
|
85
|
+
const workspaceNames = new Set()
|
|
86
|
+
let changed
|
|
87
|
+
for (const workspace of workspaces) {
|
|
88
|
+
//REVISIT: workspaces must only use simple glob like 'foo*', nested folders are not supported
|
|
89
|
+
const regex = new RegExp('^' + workspace.split('/').pop().replace(/\*/g, '.*') + '$')
|
|
90
|
+
for (const folder of folders) {
|
|
91
|
+
if (!workspaceNames.has(folder) && folder.match(regex)) {
|
|
92
|
+
workspaceNames.add(folder)
|
|
93
|
+
const plugin = require(require.resolve(path.join(workspaceRoot, folder, 'package.json')))
|
|
94
|
+
const tarFile = `${plugin.name.replace(/\//g,'-').replace(/@/, '')}-${plugin.version}.tgz`
|
|
95
|
+
if (dependencies[plugin.name] === '*') {
|
|
96
|
+
const tarFilePath = path.join(dest, tarFile)
|
|
97
|
+
dependencies[plugin.name] = `file:${tarFile}`
|
|
98
|
+
await tar.czfd(tarFilePath, path.join(workspaceRoot, folder))
|
|
99
|
+
this.pushFile(tarFilePath)
|
|
100
|
+
changed = true
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (changed) {
|
|
106
|
+
return this.write(JSON.stringify(packageJson, null, ' ')).to(path.join(dest, 'package.json'))
|
|
107
|
+
}
|
|
78
108
|
}
|
|
79
|
-
} catch (e) {
|
|
109
|
+
} catch (e) {
|
|
110
|
+
this.pushMessage(`Failed to add npm workspace dependencies: ${e.message}`)
|
|
111
|
+
this.logger.debug(e)
|
|
112
|
+
}
|
|
80
113
|
}
|
|
81
114
|
|
|
82
115
|
async clean() {
|
|
@@ -91,7 +124,7 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
91
124
|
|
|
92
125
|
async _copyNativeContent(srcRoot, srcSrv, destRoot, destSrv) {
|
|
93
126
|
// project/srv/** -> 'gen/srv/srv/**'
|
|
94
|
-
const filesFilter = await this.copySrvContent(srcSrv, destRoot, this.hasBuildOption('flatDeployLayout', true)? destRoot : destSrv)
|
|
127
|
+
const filesFilter = await this.copySrvContent(srcSrv, destRoot, this.hasBuildOption('flatDeployLayout', true) ? destRoot : destSrv)
|
|
95
128
|
|
|
96
129
|
// project/* -> 'gen/srv/*'
|
|
97
130
|
await this.copyProjectRootContent(srcRoot, destRoot, (entry) => !filesFilter.includes(path.basename(entry)))
|
package/lib/build/util.js
CHANGED
|
@@ -119,6 +119,14 @@ function getDefaultModelOptions() {
|
|
|
119
119
|
return _pushModelPaths(modelPaths)
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/**
|
|
123
|
+
* For valid paths replace '\\' with '/' and remove trailing '/'. Otherwise return as is
|
|
124
|
+
* @param {*} dir
|
|
125
|
+
*/
|
|
126
|
+
function normalizePath(dir) {
|
|
127
|
+
return typeof dir === "string" ? dir.replace(/\\/g, '/').replace(/\/$/, '') : dir
|
|
128
|
+
}
|
|
129
|
+
|
|
122
130
|
function _pushModelPaths(...modelPaths) {
|
|
123
131
|
const model = new Set()
|
|
124
132
|
// may contain nested arrays
|
|
@@ -131,7 +139,7 @@ function _pushModelPaths(...modelPaths) {
|
|
|
131
139
|
if (roots.includes(m)) {
|
|
132
140
|
const dir = path.resolve(cds.root, m)
|
|
133
141
|
if (fs.existsSync(dir)) {
|
|
134
|
-
model.add(m
|
|
142
|
+
model.add(m)
|
|
135
143
|
} else if (fs.existsSync(dir + FILE_EXT_CDS)) { //might be cds file name, compatibility to old build configs
|
|
136
144
|
model.add(m)
|
|
137
145
|
}
|
|
@@ -140,7 +148,7 @@ function _pushModelPaths(...modelPaths) {
|
|
|
140
148
|
}
|
|
141
149
|
}
|
|
142
150
|
})
|
|
143
|
-
return [...model]
|
|
151
|
+
return [...model].map(m => normalizePath(m))
|
|
144
152
|
}
|
|
145
153
|
|
|
146
154
|
function flatten(modelPaths) {
|
|
@@ -154,22 +162,6 @@ function flatten(modelPaths) {
|
|
|
154
162
|
}, [])
|
|
155
163
|
}
|
|
156
164
|
|
|
157
|
-
/**
|
|
158
|
-
* Copy a file or directory. The directory can have contents.
|
|
159
|
-
* REVISIT: 'fs.promises.cp' replacement for nodejs 14
|
|
160
|
-
* @param src <String> Note that if src is a directory it will copy everything inside of this directory, not the entire directory itself.
|
|
161
|
-
* @param dest <String> Note that if src is a file, dest cannot be a directory.
|
|
162
|
-
*/
|
|
163
|
-
async function copy(src, dest) {
|
|
164
|
-
if ((await fs.promises.stat(src)).isDirectory()) {
|
|
165
|
-
const entries = await fs.promises.readdir(src)
|
|
166
|
-
return Promise.all(entries.map(async each => copy(path.join(src, each), path.join(dest, each))))
|
|
167
|
-
} else {
|
|
168
|
-
await fs.promises.mkdir(path.dirname(dest), { recursive: true })
|
|
169
|
-
return fs.promises.copyFile(src, dest)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
165
|
function getI18nDefaultFolder() {
|
|
174
166
|
// keep '_i18n' as preferred folder for compatibility reasons
|
|
175
167
|
if (cds.env.i18n.folders.includes('_i18n')) return '_i18n'
|
|
@@ -276,9 +268,9 @@ module.exports = {
|
|
|
276
268
|
resolveRequiredSapModels,
|
|
277
269
|
getDefaultModelOptions,
|
|
278
270
|
flatten,
|
|
279
|
-
copy,
|
|
280
271
|
getI18nDefaultFolder,
|
|
281
272
|
isClassicMtx,
|
|
273
|
+
normalizePath,
|
|
282
274
|
BuildMessage,
|
|
283
275
|
BuildError
|
|
284
276
|
}
|
|
@@ -52,6 +52,7 @@ module.exports.csdl2openapi = function (
|
|
|
52
52
|
{
|
|
53
53
|
url: serviceRoot,
|
|
54
54
|
servers: serversObject,
|
|
55
|
+
odataVersion: odataVersion,
|
|
55
56
|
scheme: scheme = 'https',
|
|
56
57
|
host: host = 'localhost',
|
|
57
58
|
basePath: basePath = '/service-root',
|
|
@@ -61,6 +62,7 @@ module.exports.csdl2openapi = function (
|
|
|
61
62
|
) {
|
|
62
63
|
// as preProcess below mutates the csdl, copy it before, to avoid side-effects on the caller side
|
|
63
64
|
csdl = JSON.parse(JSON.stringify(csdl))
|
|
65
|
+
csdl.$Version = odataVersion ? odataVersion : '4.01'
|
|
64
66
|
serviceRoot = serviceRoot || (scheme + '://' + host + basePath);
|
|
65
67
|
const queryOptionPrefix = csdl.$Version <= '4.0' ? '$' : '';
|
|
66
68
|
const typesToInline = {}; // filled in schema() and used in inlineTypes()
|
|
@@ -82,7 +84,7 @@ module.exports.csdl2openapi = function (
|
|
|
82
84
|
openapi: '3.0.2',
|
|
83
85
|
info: getInfo(csdl, entityContainer),
|
|
84
86
|
'x-sap-api-type': 'ODATAV4',
|
|
85
|
-
'x-odata-version':
|
|
87
|
+
'x-odata-version': csdl.$Version,
|
|
86
88
|
'x-sap-shortText': getShortText(csdl, entityContainer),
|
|
87
89
|
servers: getServers(serviceRoot, serversObject),
|
|
88
90
|
tags: entityContainer ? getTags(entityContainer) : {},
|
|
@@ -638,12 +640,12 @@ module.exports.csdl2openapi = function (
|
|
|
638
640
|
*/
|
|
639
641
|
function operationCreate(pathItem, element, name, sourceName, targetName, target, level, restrictions) {
|
|
640
642
|
const insertRestrictions = restrictions.InsertRestrictions || target && target[voc.Capabilities.InsertRestrictions] || {};
|
|
641
|
-
|
|
643
|
+
let countRestrictions = target && (target[voc.Capabilities.CountRestrictions]?.Countable === false); // count property will be added if CountRestrictions is false
|
|
642
644
|
if (insertRestrictions.Insertable !== false) {
|
|
643
645
|
const lname = pluralize.singular(splitName(name));
|
|
644
646
|
const type = modelElement(element.$Type);
|
|
645
647
|
pathItem.post = {
|
|
646
|
-
summary: insertRestrictions.Description || operationSummary('
|
|
648
|
+
summary: insertRestrictions.Description || operationSummary('Creates', name, sourceName, level, true, true),
|
|
647
649
|
tags: [sourceName],
|
|
648
650
|
requestBody: {
|
|
649
651
|
description: type && type[voc.Core.Description] || 'New ' + lname,
|
|
@@ -653,8 +655,8 @@ module.exports.csdl2openapi = function (
|
|
|
653
655
|
schema: ref(element.$Type, SUFFIX.create),
|
|
654
656
|
}
|
|
655
657
|
}
|
|
656
|
-
},
|
|
657
|
-
responses: response(201, 'Created ' + lname, { $Type: element.$Type }, insertRestrictions.ErrorResponses),
|
|
658
|
+
},
|
|
659
|
+
responses: response(201, 'Created ' + lname, { $Type: element.$Type }, insertRestrictions.ErrorResponses, !countRestrictions),
|
|
658
660
|
};
|
|
659
661
|
if (insertRestrictions.LongDescription) pathItem.post.description = insertRestrictions.LongDescription;
|
|
660
662
|
if (targetName && sourceName != targetName) pathItem.post.tags.push(targetName);
|
|
@@ -711,6 +713,7 @@ module.exports.csdl2openapi = function (
|
|
|
711
713
|
const readRestrictions = restrictions.ReadRestrictions || targetRestrictions || {};
|
|
712
714
|
const readByKeyRestrictions = readRestrictions.ReadByKeyRestrictions;
|
|
713
715
|
let readable = true;
|
|
716
|
+
let countRestrictions = target && (target[voc.Capabilities.CountRestrictions]?.Countable === false);
|
|
714
717
|
if (byKey && readByKeyRestrictions && readByKeyRestrictions.Readable !== undefined)
|
|
715
718
|
readable = readByKeyRestrictions.Readable;
|
|
716
719
|
else if (readRestrictions.Readable !== undefined)
|
|
@@ -722,11 +725,11 @@ module.exports.csdl2openapi = function (
|
|
|
722
725
|
const lname = splitName(name);
|
|
723
726
|
const collection = !byKey && element.$Collection;
|
|
724
727
|
const operation = {
|
|
725
|
-
summary: descriptions.Description || operationSummary('
|
|
728
|
+
summary: descriptions.Description || operationSummary('Retrieves', name, sourceName, level, element.$Collection, byKey),
|
|
726
729
|
tags: [sourceName],
|
|
727
730
|
parameters: [],
|
|
728
731
|
responses: response(200, 'Retrieved ' + (byKey ? pluralize.singular(lname) : lname), { $Type: element.$Type, $Collection: collection },
|
|
729
|
-
byKey ? readByKeyRestrictions?.ErrorResponses : readRestrictions?.ErrorResponses)
|
|
732
|
+
byKey ? readByKeyRestrictions?.ErrorResponses : readRestrictions?.ErrorResponses, !countRestrictions)
|
|
730
733
|
};
|
|
731
734
|
const deltaSupported = element[voc.Capabilities.ChangeTracking] && element[voc.Capabilities.ChangeTracking].Supported;
|
|
732
735
|
if (!byKey && deltaSupported) {
|
|
@@ -1165,11 +1168,11 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1165
1168
|
*/
|
|
1166
1169
|
function operationUpdate(pathItem, element, name, sourceName, target, level, restrictions, byKey) {
|
|
1167
1170
|
const updateRestrictions = restrictions.UpdateRestrictions || target && target[voc.Capabilities.UpdateRestrictions] || {};
|
|
1168
|
-
|
|
1171
|
+
let countRestrictions = target && (target[voc.Capabilities.CountRestrictions]?.Countable === false);
|
|
1169
1172
|
if (updateRestrictions.Updatable !== false) {
|
|
1170
1173
|
const type = modelElement(element.$Type);
|
|
1171
1174
|
const operation = {
|
|
1172
|
-
summary: updateRestrictions.Description || operationSummary('
|
|
1175
|
+
summary: updateRestrictions.Description || operationSummary('Changes', name, sourceName, level, element.$Collection, byKey),
|
|
1173
1176
|
tags: [sourceName],
|
|
1174
1177
|
requestBody: {
|
|
1175
1178
|
description: type && type[voc.Core.Description] || 'New property values',
|
|
@@ -1180,7 +1183,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1180
1183
|
}
|
|
1181
1184
|
}
|
|
1182
1185
|
},
|
|
1183
|
-
responses: response(204, "Success", undefined, updateRestrictions.ErrorResponses),
|
|
1186
|
+
responses: response(204, "Success", undefined, updateRestrictions.ErrorResponses, !countRestrictions),
|
|
1184
1187
|
};
|
|
1185
1188
|
if (updateRestrictions.LongDescription) operation.description = updateRestrictions.LongDescription;
|
|
1186
1189
|
customParameters(operation, updateRestrictions);
|
|
@@ -1202,12 +1205,12 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1202
1205
|
*/
|
|
1203
1206
|
function operationDelete(pathItem, element, name, sourceName, target, level, restrictions, byKey) {
|
|
1204
1207
|
const deleteRestrictions = restrictions.DeleteRestrictions || target && target[voc.Capabilities.DeleteRestrictions] || {};
|
|
1205
|
-
|
|
1208
|
+
let countRestrictions = target && (target[voc.Capabilities.CountRestrictions]?.Countable === false);
|
|
1206
1209
|
if (deleteRestrictions.Deletable !== false) {
|
|
1207
1210
|
pathItem.delete = {
|
|
1208
|
-
summary: deleteRestrictions.Description || operationSummary('
|
|
1211
|
+
summary: deleteRestrictions.Description || operationSummary('Deletes', name, sourceName, level, element.$Collection, byKey),
|
|
1209
1212
|
tags: [sourceName],
|
|
1210
|
-
responses: response(204, "Success", undefined, deleteRestrictions.ErrorResponses),
|
|
1213
|
+
responses: response(204, "Success", undefined, deleteRestrictions.ErrorResponses, !countRestrictions),
|
|
1211
1214
|
};
|
|
1212
1215
|
if (deleteRestrictions.LongDescription) pathItem.delete.description = deleteRestrictions.LongDescription;
|
|
1213
1216
|
customParameters(pathItem.delete, deleteRestrictions);
|
|
@@ -1423,7 +1426,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1423
1426
|
const name = actionName.indexOf('.') === -1 ? actionName : nameParts(actionName).name;
|
|
1424
1427
|
const pathItem = {
|
|
1425
1428
|
post: {
|
|
1426
|
-
summary: actionImport[voc.Core.Description] || overload[voc.Core.Description] || '
|
|
1429
|
+
summary: actionImport[voc.Core.Description] || overload[voc.Core.Description] || 'Invokes action ' + name,
|
|
1427
1430
|
tags: [overload[voc.Common.Label] || sourceName || 'Service Operations'],
|
|
1428
1431
|
responses: overload.$ReturnType ? response(200, "Success", overload.$ReturnType, overload[voc.Capabilities.OperationRestrictions]?.ErrorResponses)
|
|
1429
1432
|
: response(204, "Success", undefined, overload[voc.Capabilities.OperationRestrictions]?.ErrorResponses),
|
|
@@ -1479,7 +1482,6 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1479
1482
|
const name = functionName.indexOf('.') === -1 ? functionName : nameParts(functionName).name;
|
|
1480
1483
|
let parameters = overload.$Parameter || [];
|
|
1481
1484
|
if (overload.$IsBound) parameters = parameters.slice(1);
|
|
1482
|
-
|
|
1483
1485
|
const pathSegments = [];
|
|
1484
1486
|
const params = [];
|
|
1485
1487
|
|
|
@@ -1534,7 +1536,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1534
1536
|
const pathParameters = implicitAliases ? '' : '(' + pathSegments.join(',') + ')';
|
|
1535
1537
|
const pathItem = {
|
|
1536
1538
|
get: {
|
|
1537
|
-
summary: functionImport[voc.Core.Description] || overload[voc.Core.Description] || '
|
|
1539
|
+
summary: functionImport[voc.Core.Description] || overload[voc.Core.Description] || 'Invokes function ' + name,
|
|
1538
1540
|
tags: [overload[voc.Common.Label] || sourceName || 'Service Operations'],
|
|
1539
1541
|
parameters: prefixParameters.concat(params),
|
|
1540
1542
|
responses: response(200, "Success", overload.$ReturnType, overload[voc.Capabilities.OperationRestrictions]?.ErrorResponses),
|
|
@@ -1558,7 +1560,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1558
1560
|
const firstEntitySet = Object.keys(container).filter(child => isIdentifier(child) && container[child].$Collection)[0];
|
|
1559
1561
|
paths['/$batch'] = {
|
|
1560
1562
|
post: {
|
|
1561
|
-
summary: batchSupport[voc.Core.Description] || '
|
|
1563
|
+
summary: batchSupport[voc.Core.Description] || 'Sends a group of requests',
|
|
1562
1564
|
description: (batchSupport[voc.Core.LongDescription] || 'Group multiple requests into a single request payload, see '
|
|
1563
1565
|
+ '[Batch Requests](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_BatchRequests).')
|
|
1564
1566
|
+ '\n\n*Please note that "Try it out" is not supported for this request.*',
|
|
@@ -1613,11 +1615,12 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1613
1615
|
* @param {object} type Response type object
|
|
1614
1616
|
* @param {array} errors Array of operation-specific status codes with descriptions
|
|
1615
1617
|
*/
|
|
1616
|
-
function response(code, description, type, errors) {
|
|
1618
|
+
function response(code, description, type, errors, isCount = true) {
|
|
1617
1619
|
const r = {};
|
|
1618
1620
|
r[code] = {
|
|
1619
1621
|
description: description
|
|
1620
1622
|
};
|
|
1623
|
+
let CountPropertyObj = {[csdl.$Version > '4.0' ? '@count' : '@odata.count'] : ref('count')} ;
|
|
1621
1624
|
if (code != 204) {
|
|
1622
1625
|
const s = getSchema(type);
|
|
1623
1626
|
r[code].content = {
|
|
@@ -1627,7 +1630,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1627
1630
|
type: 'object',
|
|
1628
1631
|
title: 'Collection of ' + nameParts(type.$Type || 'Edm.String').name,
|
|
1629
1632
|
properties: {
|
|
1630
|
-
|
|
1633
|
+
...(isCount && CountPropertyObj),
|
|
1631
1634
|
value: s
|
|
1632
1635
|
}
|
|
1633
1636
|
}
|
|
@@ -1806,14 +1809,20 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1806
1809
|
const isKey = keyMap(type);
|
|
1807
1810
|
const required = Object.keys(isKey);
|
|
1808
1811
|
const schemaProperties = {};
|
|
1809
|
-
|
|
1812
|
+
let isCount = true;
|
|
1813
|
+
if (csdl[qualifier]?.$Annotations) {
|
|
1814
|
+
const annotations = csdl[qualifier].$Annotations[`${qualifier}.EntityContainer/${name}`];
|
|
1815
|
+
if (annotations && annotations[voc.Capabilities.CountRestrictions] && annotations[voc.Capabilities.CountRestrictions]?.Countable === false) {
|
|
1816
|
+
isCount = false;
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1810
1819
|
const properties = propertiesOfStructuredType(type);
|
|
1811
1820
|
Object.keys(properties).forEach(iName => {
|
|
1812
1821
|
const property = properties[iName];
|
|
1813
1822
|
if (suffix === SUFFIX.read) schemaProperties[iName] = getSchema(property);
|
|
1814
1823
|
if ((Object.prototype.hasOwnProperty.call(property, '@Common.FieldControl')) && property['@Common.FieldControl'] === 'Mandatory') { required.push(iName) }
|
|
1815
1824
|
if (property.$Kind == 'NavigationProperty') {
|
|
1816
|
-
if (property.$Collection && suffix === "") {
|
|
1825
|
+
if (property.$Collection && suffix === "" && isCount === true) {
|
|
1817
1826
|
schemaProperties[`${iName}@${csdl.$Version === '4.0' ? 'odata.' : ''}count`] = ref('count');
|
|
1818
1827
|
}
|
|
1819
1828
|
if (property[voc.Core.Permissions] != "Read" && !property[voc.Core.Computed] && property.$ContainsTarget) {
|
|
@@ -34,11 +34,15 @@ function _getOpenApi(csdl, options) {
|
|
|
34
34
|
return openapi;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
function toOpenApiOptions(csdl, csn, options={}) {
|
|
37
|
+
function toOpenApiOptions(csdl, csn, options = {}) {
|
|
38
38
|
const result = {}
|
|
39
39
|
for (const key in options) {
|
|
40
|
-
if (/^openapi:(.*)/.test(key) && RegExp.$1)
|
|
41
|
-
result[RegExp.$1] = options[key]
|
|
40
|
+
if (/^openapi:(.*)/.test(key) && RegExp.$1) {
|
|
41
|
+
result[RegExp.$1] = options[key];
|
|
42
|
+
}
|
|
43
|
+
else if (key === 'odata-version') {
|
|
44
|
+
result['odataVersion'] = options[key];
|
|
45
|
+
}
|
|
42
46
|
}
|
|
43
47
|
if (result.url) {
|
|
44
48
|
result.url = result.url.replace(/\/*\$\{service-path\}/g, servicePath(csdl, csn))
|
package/lib/import/common.js
CHANGED
|
@@ -7,7 +7,7 @@ const messages = require("./message").getMessages();
|
|
|
7
7
|
const cds = require('../cds');
|
|
8
8
|
const { fs, path, write, mkdirp, exists, read } = cds.utils;
|
|
9
9
|
const cdsCompiler = require('..').compile.to;
|
|
10
|
-
const md5 = require('md5');
|
|
10
|
+
const md5 = data => require('crypto').createHash('md5').update(data).digest('hex');
|
|
11
11
|
|
|
12
12
|
let optionsMapping = {
|
|
13
13
|
'keep-namespace': 'keepNamespace',
|
package/lib/import/message.js
CHANGED
|
@@ -29,7 +29,12 @@ const messages = {
|
|
|
29
29
|
UNSUPPORTED_PROPERTY_TYPE: 'Property cannot be of EntityType. Check the type: ',
|
|
30
30
|
UNRESOLVED_SCHEMA_NAMESPACE: `Could not resolve the schema `,
|
|
31
31
|
INVALID_DATATYPE: `Invalid Data Type `,
|
|
32
|
-
ELEMENT_NAME_COLLISION: 'Collision between EntityContainer Element Name and Schema Element Name is currenlty not supported. Please check the element name: '
|
|
32
|
+
ELEMENT_NAME_COLLISION: 'Collision between EntityContainer Element Name and Schema Element Name is currenlty not supported. Please check the element name: ',
|
|
33
|
+
NO_DEFAULT_FOR_COLLECTIONS: 'WARNING: Collection type cannot have a Default value.',
|
|
34
|
+
BINARY_MAXLENGTH: 'WARNING: MaxLength for type Edm.Binary should not exceed 5000.',
|
|
35
|
+
SCALE_GT_PRECISION: 'WARNING: Scale value should not be greater than Precision value.',
|
|
36
|
+
FIXED_PRECISION_VARIABLE_SCALE: 'WARNING: Fixed Precision with Variable or Floating Scale is not supported in CDS.',
|
|
37
|
+
NULLABLE_KEY: 'WARNING: Key element cannot be Nullable.'
|
|
33
38
|
};
|
|
34
39
|
|
|
35
40
|
function getMessages() {
|
|
@@ -450,22 +450,26 @@ class Expression {
|
|
|
450
450
|
XML: If no value is specified, the Scale facet defaults to zero.
|
|
451
451
|
JSON: If no value is specified, the Scale facet defaults to zero
|
|
452
452
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
0
|
|
458
|
-
|
|
453
|
+
OLD NEW
|
|
454
|
+
XML | JSON XML | JSON
|
|
455
|
+
--------------------- -----------------------
|
|
456
|
+
variable | variable variable | variable
|
|
457
|
+
1-* | 1-* 0-* | 0-*
|
|
458
|
+
0 | undefined undefined | undefined
|
|
459
|
+
undefined | undefined
|
|
460
|
+
|
|
461
|
+
* In case EDMX doesn't specify Scale, we are not adding default `0` for it anymore
|
|
462
|
+
* beause we want to easily identify and preserve the explicitly mentioned Scale="0" in EDMX
|
|
459
463
|
*/
|
|
460
464
|
if (args.includes('$Scale') && (target.$Type === 'Edm.Decimal' || target.$UnderlyingType === 'Edm.Decimal')) {
|
|
461
465
|
if (attribs.Scale != null) {
|
|
462
466
|
if (attribs.Scale === 'variable' || attribs.Scale === 'floating') {
|
|
463
467
|
target.$Scale = attribs.Scale;
|
|
464
|
-
}
|
|
468
|
+
}
|
|
469
|
+
// for now considering only non-negative integers
|
|
470
|
+
else if (Number(attribs.Scale) >= 0) {
|
|
465
471
|
target.$Scale = parseInt(attribs.Scale, 10);
|
|
466
472
|
}
|
|
467
|
-
} else {
|
|
468
|
-
target.$Scale = 0;
|
|
469
473
|
}
|
|
470
474
|
}
|
|
471
475
|
|
|
@@ -190,10 +190,11 @@ function _updateElementObjectForDefaultValue(dataTypeObj, defaultvalue) {
|
|
|
190
190
|
// for collection, default value isin't expected
|
|
191
191
|
if (dataTypeObj.items === undefined) {
|
|
192
192
|
dataTypeObj.default = {};
|
|
193
|
-
|
|
193
|
+
// defaultvalue could be of type string (eg., '0') or number (eg., 0 or -1.5)
|
|
194
|
+
dataTypeObj.default.val = (defaultvalue !== undefined) ? defaultvalue : null;
|
|
194
195
|
}
|
|
195
196
|
else {
|
|
196
|
-
console.log(warn(
|
|
197
|
+
console.log(warn(messages.NO_DEFAULT_FOR_COLLECTIONS));
|
|
197
198
|
}
|
|
198
199
|
}
|
|
199
200
|
|
|
@@ -204,22 +205,29 @@ function _updateDataTypeObjectForMaxLength(dataTypeObj, propertyDataType, maxLen
|
|
|
204
205
|
dataTypeObj.type = mappedValue
|
|
205
206
|
dataTypeObj.length = maxLength;
|
|
206
207
|
}
|
|
207
|
-
if (propertyDataType === 'Edm.Binary' && maxLength
|
|
208
|
-
console.log(warn(
|
|
208
|
+
if (propertyDataType === 'Edm.Binary' && maxLength > 5000) {
|
|
209
|
+
console.log(warn(messages.BINARY_MAXLENGTH));
|
|
209
210
|
}
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
function _updateDataTypeObjectForPrecision(dataTypeObj, propertyDataType, precision, scale) {
|
|
213
214
|
const mappedValue = edmToCdsTypeMappingWithPrecision[propertyDataType];
|
|
215
|
+
const numberedScale = Number(scale);
|
|
214
216
|
|
|
215
217
|
if (mappedValue) {
|
|
216
218
|
dataTypeObj.type = mappedValue;
|
|
217
|
-
if (
|
|
219
|
+
if (precision >= 0) {
|
|
218
220
|
dataTypeObj.precision = precision;
|
|
219
221
|
}
|
|
220
|
-
if (
|
|
222
|
+
if (numberedScale >= 0) {
|
|
221
223
|
dataTypeObj.scale = scale;
|
|
222
224
|
}
|
|
225
|
+
if (numberedScale > precision) {
|
|
226
|
+
console.log(warn(messages.SCALE_GT_PRECISION));
|
|
227
|
+
}
|
|
228
|
+
if (precision >= 0 && (scale === 'floating' || scale === 'variable')) {
|
|
229
|
+
console.log(warn(messages.FIXED_PRECISION_VARIABLE_SCALE));
|
|
230
|
+
}
|
|
223
231
|
}
|
|
224
232
|
}
|
|
225
233
|
|
|
@@ -469,7 +477,7 @@ function _generatePrimitiveDataTypeCsn(propertyObject, isPrimaryKey, parserConte
|
|
|
469
477
|
if (isPrimaryKey) {
|
|
470
478
|
dataTypeObj.key = true;
|
|
471
479
|
if (nullable) {
|
|
472
|
-
console.log(warn(
|
|
480
|
+
console.log(warn(messages.NULLABLE_KEY));
|
|
473
481
|
}
|
|
474
482
|
}
|
|
475
483
|
_captureAnnotationWithinElement(propertyObject, dataTypeObj, parserContext);
|
|
@@ -479,7 +487,11 @@ function _generatePrimitiveDataTypeCsn(propertyObject, isPrimaryKey, parserConte
|
|
|
479
487
|
if (maxLength) {
|
|
480
488
|
_updateDataTypeObjectForMaxLength(dataTypeObj, propertyDataType, maxLength);
|
|
481
489
|
}
|
|
482
|
-
|
|
490
|
+
/**
|
|
491
|
+
* Precision: non-negative integers
|
|
492
|
+
* Scale: non-negative integers, variable, floating
|
|
493
|
+
*/
|
|
494
|
+
if (precision >= 0 || scale !== undefined) {
|
|
483
495
|
_updateDataTypeObjectForPrecision(dataTypeObj, propertyDataType, precision, scale);
|
|
484
496
|
}
|
|
485
497
|
if (edmToCdsTypeSpecialHandling[propertyDataType]) {
|
|
@@ -503,7 +515,8 @@ function _generatePrimitiveDataTypeCsn(propertyObject, isPrimaryKey, parserConte
|
|
|
503
515
|
if (isCollection) {
|
|
504
516
|
_updateElementObjectForCollection(dataTypeObj, isPrimaryKey, propertyDataType);
|
|
505
517
|
}
|
|
506
|
-
|
|
518
|
+
// defaultvalue could be of type string (eg., '0') or number (eg., 0 or -1.5)
|
|
519
|
+
if (defaultvalue !== undefined) {
|
|
507
520
|
_updateElementObjectForDefaultValue(dataTypeObj, defaultvalue);
|
|
508
521
|
}
|
|
509
522
|
|
|
@@ -538,7 +551,7 @@ function _generateStructuredDataTypeCsn(dataType, propertyObject, isPrimaryKey,
|
|
|
538
551
|
if (isPrimaryKey) {
|
|
539
552
|
dataTypeObj.key = true;
|
|
540
553
|
if (nullable) {
|
|
541
|
-
console.log(warn(
|
|
554
|
+
console.log(warn(messages.NULLABLE_KEY));
|
|
542
555
|
}
|
|
543
556
|
}
|
|
544
557
|
|
|
@@ -566,9 +579,10 @@ function _generateStructuredDataTypeCsn(dataType, propertyObject, isPrimaryKey,
|
|
|
566
579
|
* but compiler accepts it for TypeDefintion and ComplexType as well,
|
|
567
580
|
* do we also need to handle it for TypeDefintion and ComplexType?
|
|
568
581
|
*
|
|
569
|
-
* For parameters,
|
|
582
|
+
* For parameters, defaultvalue is added via OptionalParamter Annotation
|
|
583
|
+
* defaultvalue could be of type string (eg., '0') or number (eg., 0 or -1.5)
|
|
570
584
|
*/
|
|
571
|
-
if (defaultvalue && dataTypeKind === 'EnumType' && elementKind === 'property') {
|
|
585
|
+
if (defaultvalue !== undefined && dataTypeKind === 'EnumType' && elementKind === 'property') {
|
|
572
586
|
_updateElementObjectForDefaultValue(dataTypeObj, defaultvalue);
|
|
573
587
|
}
|
|
574
588
|
else {
|
package/lib/init/constants.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const MAVEN_ARCHETYPE_VERSION = '2.0.2';
|
|
4
|
-
|
|
5
|
-
const constants = {
|
|
6
|
-
IS_WIN: (os.platform() === 'win32'),
|
|
1
|
+
module.exports = {
|
|
2
|
+
IS_WIN: require('os').platform() === 'win32',
|
|
7
3
|
|
|
8
4
|
COMMAND_INIT: 'init',
|
|
9
5
|
COMMAND_ADD: 'add',
|
|
10
6
|
|
|
11
|
-
MAVEN_ARCHETYPE_VERSION,
|
|
7
|
+
MAVEN_ARCHETYPE_VERSION: '2.2.0',
|
|
12
8
|
|
|
13
9
|
OPTIONS: Object.freeze({
|
|
14
10
|
JAVA: 'java',
|
|
@@ -29,7 +25,7 @@ const constants = {
|
|
|
29
25
|
XSUAA: 'xsuaa',
|
|
30
26
|
HANA: 'hana',
|
|
31
27
|
SQLITE: 'sqlite',
|
|
32
|
-
|
|
28
|
+
H2: 'h2',
|
|
33
29
|
ENTERPRISE_MESSAGING: 'enterprise-messaging',
|
|
34
30
|
ENTERPRISE_MESSAGING_SHARED: 'enterprise-messaging-shared',
|
|
35
31
|
FILE_BASED_MESSAGING: 'file-based-messaging',
|
|
@@ -48,6 +44,8 @@ const constants = {
|
|
|
48
44
|
PROJECT_TYPE: Object.freeze({ nodejs: 1, java: 2, unknown: 3 }),
|
|
49
45
|
|
|
50
46
|
REGEX_JAVA_PACKAGE: /^[a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)*$/g,
|
|
47
|
+
// copied from mbt error message
|
|
48
|
+
REGEX_JAVA_VERSION: /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(-(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/,
|
|
51
49
|
REGEX_PROJECT_NAME: /[^0-9a-zA-Z_-]/g,
|
|
52
50
|
|
|
53
51
|
URLS: Object.freeze({
|
|
@@ -57,11 +55,8 @@ const constants = {
|
|
|
57
55
|
MAVEN_ARCHETYPE_HELP: `https://cap.cloud.sap/docs/java/development/#the-maven-archetype`,
|
|
58
56
|
MAVEN_INSTALL_HELP: `https://maven.apache.org/install.html`,
|
|
59
57
|
VSCODE: 'https://code.visualstudio.com/',
|
|
60
|
-
VSCODE_NBVIEWER: 'https://marketplace.visualstudio.com/items?itemName=ms-python.python',
|
|
61
58
|
VSCODE_ESLINT: 'https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint'
|
|
62
59
|
}),
|
|
63
60
|
|
|
64
61
|
PROJECT_FILES: ['package.json', 'pom.xml', '.cdsrc.json']
|
|
65
62
|
}
|
|
66
|
-
|
|
67
|
-
module.exports = constants;
|