@sap/cds 6.6.2 → 6.7.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/CHANGELOG.md +59 -2
- package/README.md +1 -1
- package/apis/connect.d.ts +11 -4
- package/apis/core.d.ts +1 -1
- package/apis/csn.d.ts +1 -0
- package/apis/internal/inference.d.ts +15 -2
- package/apis/log.d.ts +10 -0
- package/apis/serve.d.ts +4 -9
- package/apis/services.d.ts +86 -19
- package/bin/build/buildTaskEngine.js +16 -42
- package/bin/build/constants.js +4 -2
- package/bin/build/provider/buildTaskProviderInternal.js +117 -85
- package/bin/build/provider/hana/index.js +6 -1
- package/bin/build/provider/mtx-extension/index.js +74 -34
- package/bin/build/provider/mtx-sidecar/index.js +3 -3
- package/bin/build/provider/nodejs/index.js +2 -2
- package/bin/build/util.js +63 -14
- package/bin/cds-serve.js +6 -0
- package/bin/cds.js +20 -4
- package/bin/deploy/to-hana/cfUtil.js +15 -1
- package/bin/mtx/in-cds.js +2 -9
- package/bin/plugins.js +31 -0
- package/bin/serve.js +12 -12
- package/lib/compile/etc/_localized.js +1 -1
- package/lib/compile/for/lean_drafts.js +22 -6
- package/lib/compile/for/nodejs.js +4 -1
- package/lib/compile/load.js +4 -2
- package/lib/core/index.js +35 -15
- package/lib/dbs/cds-deploy.js +129 -133
- package/lib/env/cds-env.js +25 -17
- package/lib/env/cds-requires.js +10 -40
- package/lib/env/compat.js +12 -0
- package/lib/env/defaults.js +17 -9
- package/lib/env/plugins.js +29 -0
- package/lib/env/schemas/cds-rc.json +14 -0
- package/lib/index.js +3 -0
- package/lib/log/cds-log.js +7 -4
- package/lib/ql/CREATE.js +1 -1
- package/lib/ql/DELETE.js +1 -1
- package/lib/ql/DROP.js +3 -3
- package/lib/ql/INSERT.js +1 -1
- package/lib/ql/Query.js +14 -6
- package/lib/ql/SELECT.js +8 -2
- package/lib/ql/UPDATE.js +1 -1
- package/lib/ql/Whereable.js +1 -1
- package/lib/ql/cds-ql.js +1 -9
- package/lib/req/cds-context.js +1 -4
- package/lib/req/request.js +63 -2
- package/lib/req/response.js +3 -2
- package/lib/srv/bindings.js +69 -71
- package/lib/srv/cds-connect.js +4 -1
- package/lib/srv/cds-serve.js +4 -0
- package/lib/srv/middlewares/index.js +37 -6
- package/lib/srv/protocols/_legacy.js +1 -1
- package/lib/srv/protocols/index.js +1 -1
- package/lib/srv/srv-api.js +4 -6
- package/lib/srv/srv-dispatch.js +4 -3
- package/lib/srv/srv-handlers.js +1 -1
- package/lib/srv/srv-methods.js +8 -2
- package/lib/utils/cds-test.js +4 -1
- package/libx/_runtime/audit/Service.js +8 -9
- package/libx/_runtime/audit/generic/personal/index.js +1 -1
- package/libx/_runtime/audit/generic/personal/utils.js +1 -1
- package/libx/_runtime/audit/utils/v2.js +17 -20
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +2 -0
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +11 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +0 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/readToCQN.js +3 -3
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +1 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/metaInfo.js +4 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/oDataConfiguration.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +1 -1
- package/libx/_runtime/cds-services/services/Service.js +1 -1
- package/libx/_runtime/cds-services/util/assert.js +41 -65
- package/libx/_runtime/common/code-ext/WorkerPool.js +90 -0
- package/libx/_runtime/common/code-ext/WorkerReq.js +0 -4
- package/libx/_runtime/common/code-ext/execute.js +28 -18
- package/libx/_runtime/common/code-ext/handlers.js +5 -4
- package/libx/_runtime/common/code-ext/worker.js +45 -3
- package/libx/_runtime/common/code-ext/workerQueryExecutor.js +8 -7
- package/libx/_runtime/common/composition/delete.js +1 -1
- package/libx/_runtime/common/composition/update.js +3 -5
- package/libx/_runtime/common/generic/auth/expand.js +1 -1
- package/libx/_runtime/common/generic/auth/readOnly.js +5 -4
- package/libx/_runtime/common/generic/auth/restrict.js +7 -2
- package/libx/_runtime/common/generic/crud.js +12 -1
- package/libx/_runtime/common/generic/etag.js +11 -3
- package/libx/_runtime/common/generic/input.js +8 -6
- package/libx/_runtime/common/generic/paging.js +25 -8
- package/libx/_runtime/common/generic/put.js +1 -1
- package/libx/_runtime/common/generic/sorting.js +0 -1
- package/libx/_runtime/common/i18n/messages.properties +1 -0
- package/libx/_runtime/common/utils/cqn.js +5 -1
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +2 -2
- package/libx/_runtime/common/utils/resolveView.js +14 -10
- package/libx/_runtime/common/utils/rewriteAsterisks.js +2 -3
- package/libx/_runtime/common/utils/templateProcessor.js +15 -17
- package/libx/_runtime/common/utils/templateProcessorPathSerializer.js +18 -6
- package/libx/_runtime/db/Service.js +1 -0
- package/libx/_runtime/db/data-conversion/post-processing.js +0 -18
- package/libx/_runtime/db/expand/expand-v2.js +2 -2
- package/libx/_runtime/db/expand/rawToExpanded.js +6 -6
- package/libx/_runtime/db/generic/integrity.js +1 -1
- package/libx/_runtime/db/utils/columns.js +5 -5
- package/libx/_runtime/fiori/generic/activate.js +3 -3
- package/libx/_runtime/fiori/generic/edit.js +1 -1
- package/libx/_runtime/fiori/generic/new.js +4 -0
- package/libx/_runtime/fiori/lean-draft.js +138 -46
- package/libx/_runtime/hana/execute.js +3 -1
- package/libx/_runtime/hana/pool.js +10 -2
- package/libx/_runtime/messaging/common-utils/AMQPClient.js +6 -1
- package/libx/_runtime/messaging/enterprise-messaging.js +1 -0
- package/libx/_runtime/remote/Service.js +16 -13
- package/libx/_runtime/remote/utils/client.js +6 -1
- package/libx/_runtime/sqlite/Service.js +5 -59
- package/libx/_runtime/sqlite/convertDraftAdminPathExpression.js +1 -0
- package/libx/_runtime/sqlite/customBuilder/CustomUpsertBuilder.js +2 -2
- package/libx/_runtime/sqlite/execute.js +3 -1
- package/libx/_runtime/types/api.js +12 -3
- package/libx/odata/afterburner.js +36 -0
- package/libx/odata/cqn2odata.js +1 -1
- package/libx/odata/grammar.pegjs +5 -3
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +1 -1
- package/libx/rest/RestAdapter.js +1 -1
- package/libx/rest/RestRequest.js +1 -0
- package/package.json +5 -2
- package/libx/_runtime/common/code-ext/workerQuery.js +0 -45
- package/libx/_runtime/common/constants/limit.js +0 -12
- package/libx/_runtime/common/utils/page.js +0 -39
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const cds = require('../cds')
|
|
4
|
-
const { hasJavaNature, getProperty, isStreamlinedMtx, getDefaultModelOptions, BuildError } = require('../util')
|
|
4
|
+
const { hasJavaNature, getProperty, isStreamlinedMtx, getDefaultModelOptions, BuildError, hasOptionValue } = require('../util')
|
|
5
5
|
const BuildTaskProvider = require('../buildTaskProvider')
|
|
6
6
|
|
|
7
7
|
const { FILE_EXT_CDS, BUILD_TASK_HANA, BUILD_TASK_FIORI, BUILD_TASK_JAVA, BUILD_TASK_JAVA_CF, BUILD_TASK_NODEJS, BUILD_TASK_NODE_CF, BUILD_TASK_MTX,
|
|
8
|
-
BUILD_TASK_PREFIX, BUILD_TASKS, BUILD_TASK_MTX_SIDECAR, MTX_SIDECAR_FOLDER, BUILD_TASK_MTX_EXTENSION,
|
|
8
|
+
BUILD_TASK_PREFIX, BUILD_TASKS, BUILD_TASK_MTX_SIDECAR, MTX_SIDECAR_FOLDER, BUILD_TASK_MTX_EXTENSION, NODEJS_MODEL_EXCLUDE_LIST,
|
|
9
|
+
DEFAULT_BUILT_IN_MODELS } = require("../constants")
|
|
9
10
|
|
|
10
11
|
class BuildTaskProviderInternal extends BuildTaskProvider {
|
|
11
12
|
constructor(logger) {
|
|
@@ -37,41 +38,10 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
|
|
|
37
38
|
task.for = task.for || taskFor
|
|
38
39
|
task.use = task.use || `${BUILD_TASK_PREFIX}/${taskFor}`
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
case BUILD_TASK_JAVA:
|
|
45
|
-
case BUILD_TASK_JAVA_CF:
|
|
46
|
-
task.src = task.src || BuildTaskProviderInternal._normalizePath(cds.env.folders.srv)
|
|
47
|
-
if (!task.options?.model) {
|
|
48
|
-
BuildTaskProviderInternal._setDefaultModelOptionsForJava(task, projectPath)
|
|
49
|
-
}
|
|
50
|
-
break
|
|
51
|
-
case BUILD_TASK_NODEJS:
|
|
52
|
-
case BUILD_TASK_NODE_CF:
|
|
53
|
-
task.src = task.src || BuildTaskProviderInternal._normalizePath(cds.env.folders.srv)
|
|
54
|
-
break
|
|
55
|
-
case BUILD_TASK_FIORI:
|
|
56
|
-
task.src = task.src || BuildTaskProviderInternal._normalizePath(cds.env.folders.app)
|
|
57
|
-
break
|
|
58
|
-
case BUILD_TASK_MTX_SIDECAR:
|
|
59
|
-
task.src = task.src || MTX_SIDECAR_FOLDER
|
|
60
|
-
if (!task.options?.model && BuildTaskProviderInternal._hasJavaNature(projectPath, cds.env.folders.srv)) {
|
|
61
|
-
BuildTaskProviderInternal._setDefaultModelOptionsForJava(task, projectPath)
|
|
62
|
-
}
|
|
63
|
-
break
|
|
64
|
-
case BUILD_TASK_MTX_EXTENSION:
|
|
65
|
-
case BUILD_TASK_MTX:
|
|
66
|
-
if (isStreamlinedMtx()) {
|
|
67
|
-
task.src = task.src || BuildTaskProviderInternal._normalizePath(cds.env.folders.srv)
|
|
68
|
-
} else {
|
|
69
|
-
task.src = task.src || "."
|
|
70
|
-
}
|
|
71
|
-
break
|
|
72
|
-
default:
|
|
73
|
-
throw new Error(`Unknown build task '${task.use || task.for}'`)
|
|
74
|
-
}
|
|
41
|
+
BuildTaskProviderInternal._setDefaultSrcFolder(task)
|
|
42
|
+
|
|
43
|
+
// src folder needs to be initialized first
|
|
44
|
+
BuildTaskProviderInternal._setDefaultModel(task, projectPath)
|
|
75
45
|
}
|
|
76
46
|
|
|
77
47
|
async _createTasks(tasks, buildOptions, addRequiredTasks) {
|
|
@@ -136,10 +106,14 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
|
|
|
136
106
|
if (srvTask) {
|
|
137
107
|
tasks.push(srvTask)
|
|
138
108
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (
|
|
142
|
-
|
|
109
|
+
|
|
110
|
+
// REVISIT: disable for cds-dk projectReader - causing error due to inconsistent state during project creation
|
|
111
|
+
if (buildOptions.resolve !== false) {
|
|
112
|
+
// create mtx build task
|
|
113
|
+
const mtxTask = this._createMtxTask(projectPath, srv, srv, tasks)
|
|
114
|
+
if (mtxTask) {
|
|
115
|
+
tasks.push(mtxTask)
|
|
116
|
+
}
|
|
143
117
|
}
|
|
144
118
|
}
|
|
145
119
|
}
|
|
@@ -189,55 +163,60 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
|
|
|
189
163
|
}
|
|
190
164
|
|
|
191
165
|
_createMtxTask(projectPath, src, dest, tasks) {
|
|
192
|
-
// MTX build task creation is NOT supported for Java projects
|
|
193
|
-
if (tasks.some(task => task.for === BUILD_TASK_JAVA || task.for === BUILD_TASK_JAVA_CF)) {
|
|
194
|
-
return null
|
|
195
|
-
}
|
|
196
166
|
this.logger.debug("determining mtx version of nodejs project")
|
|
197
167
|
|
|
198
168
|
// preserve order of creation
|
|
199
169
|
if (BuildTaskProviderInternal._isMtxExtension()) {
|
|
200
|
-
this.logger.debug("
|
|
201
|
-
return {
|
|
202
|
-
for: BUILD_TASK_MTX_EXTENSION
|
|
203
|
-
}
|
|
170
|
+
this.logger.debug("MTX extension app")
|
|
171
|
+
return { for: BUILD_TASK_MTX_EXTENSION }
|
|
204
172
|
}
|
|
205
173
|
|
|
206
174
|
if (isStreamlinedMtx()) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
175
|
+
const sidecarPath = path.join(projectPath, MTX_SIDECAR_FOLDER)
|
|
176
|
+
let sidecarEnv
|
|
177
|
+
if (fs.existsSync(sidecarPath)) {
|
|
178
|
+
sidecarEnv = cds.env.for("cds", sidecarPath)
|
|
179
|
+
}
|
|
180
|
+
if (
|
|
181
|
+
cds.env.profiles.includes('with-mtx-sidecar') || // new presets
|
|
182
|
+
cds.env.requires["cds.xt.ModelProviderService"]?.external // for compatibility with former mtxs presets
|
|
183
|
+
) {
|
|
184
|
+
this.logger.debug("MTX app with sidecar")
|
|
185
|
+
if (!sidecarEnv) {
|
|
186
|
+
throw new BuildError(`MTX sidecar directory '${sidecarPath}' not existing. Custom build task configuration required if the folder is named differently.`)
|
|
213
187
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
for: BUILD_TASK_MTX_SIDECAR
|
|
218
|
-
}
|
|
188
|
+
if (!sidecarEnv.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
|
|
189
|
+
throw new BuildError(`Invalid MTX sidecar configuration - profile 'mtx-sidecar' not set.`)
|
|
219
190
|
}
|
|
220
|
-
|
|
191
|
+
return { for: BUILD_TASK_MTX_SIDECAR }
|
|
192
|
+
}
|
|
193
|
+
if (sidecarEnv?.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
|
|
194
|
+
throw new BuildError(`MTX sidecar configuration requires profile 'with-mtx-sidecar' or custom build task configuration.`)
|
|
221
195
|
}
|
|
222
196
|
|
|
223
|
-
if (cds.env.requires["cds.xt.ModelProviderService"]?.
|
|
197
|
+
if (cds.env.requires["cds.xt.ModelProviderService"]?._in_sidecar) {
|
|
224
198
|
// cds build is executed in sidecar folder
|
|
225
|
-
throw new BuildError(
|
|
199
|
+
throw new BuildError(`Invalid working directory ${projectPath}. Make sure to execute 'cds build' in the CAP project root directory.`)
|
|
226
200
|
}
|
|
227
201
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
202
|
+
// without sidecar
|
|
203
|
+
if (!tasks.some(task => task.for === BUILD_TASK_JAVA || task.for === BUILD_TASK_JAVA_CF)) {
|
|
204
|
+
this.logger.debug("Nodejs MTX app without sidecar")
|
|
205
|
+
return {
|
|
206
|
+
for: BUILD_TASK_MTX,
|
|
207
|
+
src
|
|
208
|
+
}
|
|
232
209
|
}
|
|
233
210
|
}
|
|
234
211
|
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
212
|
+
if (!tasks.some(task => task.for === BUILD_TASK_JAVA || task.for === BUILD_TASK_JAVA_CF)) {
|
|
213
|
+
if (cds.env.requires.multitenancy) {
|
|
214
|
+
this.logger.debug("Nodejs Classic MTX app without sidecar")
|
|
215
|
+
return {
|
|
216
|
+
src: ".",
|
|
217
|
+
for: BUILD_TASK_MTX,
|
|
218
|
+
dest
|
|
219
|
+
}
|
|
241
220
|
}
|
|
242
221
|
}
|
|
243
222
|
}
|
|
@@ -284,25 +263,78 @@ class BuildTaskProviderInternal extends BuildTaskProvider {
|
|
|
284
263
|
return !!cds.env.extends
|
|
285
264
|
}
|
|
286
265
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
266
|
+
static _setDefaultSrcFolder(task) {
|
|
267
|
+
switch (task.for) {
|
|
268
|
+
case BUILD_TASK_HANA:
|
|
269
|
+
task.src = task.src || this._normalizePath(cds.env.folders.db)
|
|
270
|
+
break
|
|
271
|
+
case BUILD_TASK_JAVA:
|
|
272
|
+
case BUILD_TASK_JAVA_CF:
|
|
273
|
+
case BUILD_TASK_NODEJS:
|
|
274
|
+
case BUILD_TASK_NODE_CF:
|
|
275
|
+
task.src = task.src || this._normalizePath(cds.env.folders.srv)
|
|
276
|
+
break
|
|
277
|
+
case BUILD_TASK_FIORI:
|
|
278
|
+
task.src = task.src || this._normalizePath(cds.env.folders.app)
|
|
279
|
+
break
|
|
280
|
+
case BUILD_TASK_MTX_SIDECAR:
|
|
281
|
+
task.src = task.src || MTX_SIDECAR_FOLDER
|
|
282
|
+
break
|
|
283
|
+
case BUILD_TASK_MTX_EXTENSION:
|
|
284
|
+
task.src = task.src || "."
|
|
285
|
+
break
|
|
286
|
+
case BUILD_TASK_MTX:
|
|
287
|
+
if (isStreamlinedMtx()) {
|
|
288
|
+
task.src = task.src || this._normalizePath(cds.env.folders.srv)
|
|
289
|
+
} else {
|
|
290
|
+
task.src = task.src || "."
|
|
291
|
+
}
|
|
292
|
+
break
|
|
293
|
+
default:
|
|
294
|
+
throw new Error(`Unknown build task '${task.use || task.for}'`)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
static _setDefaultModel(task, projectPath) {
|
|
299
|
+
let taskModelPaths = task.options?.model
|
|
300
|
+
if (taskModelPaths && !Array.isArray(taskModelPaths)) {
|
|
301
|
+
taskModelPaths = [taskModelPaths]
|
|
302
|
+
}
|
|
295
303
|
task.options = task.options || {}
|
|
296
|
-
|
|
304
|
+
let defaultModelPaths = []
|
|
305
|
+
// add default models to custom build tasks if this hasn't been disabled
|
|
306
|
+
if (taskModelPaths?.length) {
|
|
307
|
+
if (!hasOptionValue(task.options?.[DEFAULT_BUILT_IN_MODELS], false)) {
|
|
308
|
+
// get the built-in models
|
|
309
|
+
defaultModelPaths = getDefaultModelOptions(projectPath).filter(p => p.match('@sap/cds'))
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
defaultModelPaths = getDefaultModelOptions(projectPath)
|
|
313
|
+
defaultModelPaths.push(task.src)
|
|
314
|
+
if (hasOptionValue(task.options?.[DEFAULT_BUILT_IN_MODELS], false)) {
|
|
315
|
+
// all default models except the built-in models
|
|
316
|
+
defaultModelPaths = defaultModelPaths.filter(p => !p.match('@sap/cds'))
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// REVISIT: filter nodejs bootstrap service models for Java - issues/12770#issuecomment-1805719
|
|
321
|
+
if (this._hasJavaNature(projectPath, cds.env.folders.srv)) {
|
|
322
|
+
defaultModelPaths = defaultModelPaths.filter(p => !NODEJS_MODEL_EXCLUDE_LIST.includes(p))
|
|
323
|
+
}
|
|
324
|
+
// also add built-in models to custom build tasks already containing model options
|
|
325
|
+
if (!taskModelPaths || defaultModelPaths.length) {
|
|
326
|
+
task.options.model = [...new Set(taskModelPaths?.length ? taskModelPaths.concat(defaultModelPaths) : defaultModelPaths)]
|
|
327
|
+
}
|
|
297
328
|
}
|
|
298
329
|
|
|
299
330
|
/**
|
|
300
331
|
* Returns whether this project is a java project or not.
|
|
301
332
|
* @param {string} projectPath - the absolute project path
|
|
302
|
-
* @param {string}
|
|
333
|
+
* @param {string} srv - the folder name of the service module
|
|
303
334
|
*/
|
|
304
|
-
static _hasJavaNature(projectPath,
|
|
305
|
-
|
|
335
|
+
static _hasJavaNature(projectPath, srv) {
|
|
336
|
+
srv = this._getModuleFolder(projectPath, Array.isArray(srv) ? srv : [srv])
|
|
337
|
+
return hasJavaNature([projectPath, srv && path.join(projectPath, srv)])
|
|
306
338
|
}
|
|
307
339
|
|
|
308
340
|
static _getForValueFromTask(task) {
|
|
@@ -436,7 +436,12 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
436
436
|
if (fs.existsSync(file)) {
|
|
437
437
|
const undeployList = JSON.parse((await fs.promises.readFile(file)).toString(), 'utf-8')
|
|
438
438
|
if (Array.isArray(undeployList)) {
|
|
439
|
-
undeployList.forEach(entry =>
|
|
439
|
+
undeployList.forEach(entry => {
|
|
440
|
+
const extName = path.extname(entry)
|
|
441
|
+
if (extName) {
|
|
442
|
+
result.add(extName)
|
|
443
|
+
}
|
|
444
|
+
})
|
|
440
445
|
}
|
|
441
446
|
}
|
|
442
447
|
return result
|
|
@@ -3,9 +3,9 @@ const fs = require('fs')
|
|
|
3
3
|
const cds = require('../../cds')
|
|
4
4
|
|
|
5
5
|
const BuildTaskHandlerInternal = require('../buildTaskHandlerInternal')
|
|
6
|
-
const { FOLDER_GEN } = require('../../constants')
|
|
6
|
+
const { FOLDER_GEN, EXTENSION_POINT_VALIDATION } = require('../../constants')
|
|
7
7
|
const ResourcesTarBuilder = require('../mtx/resourcesTarBuilder')
|
|
8
|
-
const { BuildError } = require('../../util')
|
|
8
|
+
const { BuildError, BuildMessage } = require('../../util')
|
|
9
9
|
|
|
10
10
|
class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
11
11
|
init() {
|
|
@@ -18,43 +18,32 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
18
18
|
async build() {
|
|
19
19
|
const { src, dest } = this.task
|
|
20
20
|
const destExt = path.join(dest, 'ext')
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// validate existence of base model by simply checking existence of appPackage folder
|
|
29
|
-
// cds.resolve might fail for the extension migration use case as no index.csn file exists.
|
|
30
|
-
// A compilation error is thrown anyhow if any base model using statement cannot be resolved.
|
|
31
|
-
const appPackage = MtxExtensionModuleBuilder._getAppPackageName()
|
|
32
|
-
if (!fs.existsSync(path.join(src, 'node_modules', appPackage))) {
|
|
33
|
-
throw new BuildError(`The SaaS application base model '${appPackage}' is missing. Have you run the 'cds pull' command?`)
|
|
21
|
+
// check existence of appPackage folder only
|
|
22
|
+
// REVISIT: cds.resolve will fail as no index.csn file exists in this folder for the extension migration use case
|
|
23
|
+
// a compilation error is thrown anyhow if any base model refs cannot be resolved
|
|
24
|
+
const appPackageFolder = this._getAppPackageFolder()
|
|
25
|
+
if (!fs.existsSync(path.join(src, appPackageFolder))) {
|
|
26
|
+
throw new BuildError(`The SaaS application base model '${appPackageFolder}' is missing. Have you run the 'cds pull' command?`)
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
//
|
|
37
|
-
const folders = [path.join(src, cds.env.folders.srv, 'handlers')]
|
|
38
|
-
await this.copyNativeContent(src, destExt, res => {
|
|
39
|
-
if (fs.statSync(res).isDirectory()) {
|
|
40
|
-
return folders.some(folder => folder.startsWith(res))
|
|
41
|
-
}
|
|
42
|
-
if (folders.includes(path.dirname(res)) && /\.js$/.test(res)) {
|
|
43
|
-
return true
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
|
|
29
|
+
// full compile of the extension using base model - ensuring consistency
|
|
47
30
|
const model = await this.model()
|
|
31
|
+
|
|
48
32
|
if (model) {
|
|
49
33
|
// extension CSN using parsed format
|
|
50
34
|
const options = { ...this.options(), flavor: 'parsed' }
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
|
|
35
|
+
const extModel = await cds.load(this.resolveModel(), options)
|
|
36
|
+
if (extModel.requires) {
|
|
37
|
+
extModel.requires.length = 0
|
|
54
38
|
}
|
|
55
|
-
await this.compileToJson(extCsn, path.join(destExt, 'extension.csn'))
|
|
56
39
|
|
|
57
|
-
|
|
40
|
+
if (!this.hasBuildOption(EXTENSION_POINT_VALIDATION, false)) {
|
|
41
|
+
this._lintExtModel(extModel, model) // throws error in case of linting errors
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await this.compileToJson(extModel, path.join(destExt, 'extension.csn'))
|
|
45
|
+
|
|
46
|
+
await this.collectLanguageBundles(extModel, path.join(destExt, 'i18n'))
|
|
58
47
|
|
|
59
48
|
const files = Object.keys(await cds.deploy.resources(model))
|
|
60
49
|
if (files.length > 0) {
|
|
@@ -68,12 +57,63 @@ class MtxExtensionModuleBuilder extends BuildTaskHandlerInternal {
|
|
|
68
57
|
)
|
|
69
58
|
}
|
|
70
59
|
}
|
|
71
|
-
|
|
60
|
+
|
|
61
|
+
const packageJson = path.join(src, 'package.json')
|
|
62
|
+
if (!fs.existsSync(packageJson)) {
|
|
63
|
+
throw new BuildError(`package.json file not found in ${src}`)
|
|
64
|
+
}
|
|
65
|
+
await this.copy(packageJson).to(path.join(destExt, 'package.json'))
|
|
66
|
+
|
|
67
|
+
// copy handlers
|
|
68
|
+
const folders = [path.join(src, cds.env.folders.srv, 'handlers')]
|
|
69
|
+
await this.copyNativeContent(src, destExt, res => {
|
|
70
|
+
if (fs.statSync(res).isDirectory()) {
|
|
71
|
+
return folders.some(folder => folder.startsWith(res))
|
|
72
|
+
}
|
|
73
|
+
if (folders.includes(path.dirname(res)) && /\.js$/.test(res)) {
|
|
74
|
+
return true
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
// add all resources contained in 'gen/ext' folder
|
|
72
79
|
await new ResourcesTarBuilder(this).writeTarFile(path.join(this.task.dest, 'extension.tgz'), destExt)
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
_lintExtModel(extModel, model) {
|
|
83
|
+
const linter = this._linter()
|
|
84
|
+
if (!linter) {
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
const env = cds.env.for('cds', path.join(this.task.src, this._getAppPackageFolder()))
|
|
88
|
+
this.logger._debug && this.logger.debug(`Saas extension point restrictions:\n${env.requires?.['cds.xt.ExtensibilityService']}`)
|
|
89
|
+
|
|
90
|
+
const messages = linter.lint(extModel, model, env)
|
|
91
|
+
if (messages.length) {
|
|
92
|
+
// REVISIT: lint messages can be passed as is with cds-mtxs version >= 1.7
|
|
93
|
+
throw new BuildError('SaaS extension point restrictions violated. Check the concrete restrictions defined by the SaaS app provider.',
|
|
94
|
+
messages.map(f => new BuildMessage(f.message, f.severity, f.location || f.element?.$location)))
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
_linter() {
|
|
99
|
+
let linter
|
|
100
|
+
try {
|
|
101
|
+
// Make sure cds-mtxs APIs are loaded
|
|
102
|
+
linter = require('@sap/cds-mtxs').xt?.linter // eslint-disable-line cds/no-missing-dependencies
|
|
103
|
+
if (!linter) {
|
|
104
|
+
this.pushMessage('MTXS linter cannot be loaded. Update of @sap/cds-dk and @sap/cds-mtxs modules required? Skipping extension model lint step.')
|
|
105
|
+
return null // too old mtxs
|
|
106
|
+
}
|
|
107
|
+
} catch (e) {
|
|
108
|
+
if (e.code !== 'MODULE_NOT_FOUND') throw e
|
|
109
|
+
this.pushMessage('MTXS linter cannot be loaded, @sap/cds-mtxs not installed. Skipping extension model lint step.')
|
|
110
|
+
}
|
|
111
|
+
return linter
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
_getAppPackageFolder() {
|
|
115
|
+
return path.join('node_modules', cds.env.extends || '_base');
|
|
77
116
|
}
|
|
78
117
|
}
|
|
118
|
+
|
|
79
119
|
module.exports = MtxExtensionModuleBuilder
|
|
@@ -58,10 +58,10 @@ class MtxSidecarModuleBuilder extends NodeCfModuleBuilder {
|
|
|
58
58
|
* @param {object} sidecarEnv cds env based on the sidecar dir
|
|
59
59
|
*/
|
|
60
60
|
async _buildMainApp(sidecarEnv) {
|
|
61
|
-
if (sidecarEnv.requires['cds.xt.ModelProviderService']?.kind !== 'in-sidecar') {
|
|
62
|
-
throw new BuildError('Invalid MTX sidecar configuration - "cds.xt.ModelProviderService": "in-sidecar" missing.')
|
|
63
|
-
}
|
|
64
61
|
let main = sidecarEnv.requires['cds.xt.ModelProviderService']?.root
|
|
62
|
+
if (!main) {
|
|
63
|
+
throw new BuildError('Invalid MTX sidecar configuration - "cds/requires/cds.xt.ModelProviderService/root" missing.')
|
|
64
|
+
}
|
|
65
65
|
const profiles = cds.env.profiles || []
|
|
66
66
|
|
|
67
67
|
if (!profiles.includes("production") && !profiles.includes("prod")) {
|
|
@@ -3,7 +3,7 @@ const path = require('path')
|
|
|
3
3
|
const cds = require('../../cds')
|
|
4
4
|
const BuildTaskHandlerEdmx = require('../buildTaskHandlerEdmx')
|
|
5
5
|
const { BuildError } = require('../../util')
|
|
6
|
-
const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY, ODATA_VERSION_V2, FOLDER_GEN,
|
|
6
|
+
const { BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY, ODATA_VERSION_V2, FOLDER_GEN, BUILD_NODEJS_EDMX_GENERATION, EDMX_GENERATION,
|
|
7
7
|
SKIP_PACKAGE_JSON_GENERATION, CONTENT_EDMX, CONTENT_PACKAGE_JSON, CONTENT_PACKAGELOCK_JSON, CONTENT_NPMRC, CONTENT_CDSRC_JSON,
|
|
8
8
|
CONTENT_ENV, CONTENT_DEFAULT_ENV_JSON } = require('../../constants')
|
|
9
9
|
const { WARNING } = BuildTaskHandlerEdmx
|
|
@@ -19,7 +19,7 @@ class NodejsModuleBuilder extends BuildTaskHandlerEdmx {
|
|
|
19
19
|
this.task.options[CONTENT_CDSRC_JSON] = !this.hasBuildOption(CONTENT_CDSRC_JSON, false) ? true : false
|
|
20
20
|
|
|
21
21
|
// default value false
|
|
22
|
-
this.task.options[CONTENT_EDMX] = this.hasBuildOption(CONTENT_EDMX, true) || this.hasCdsEnvOption(
|
|
22
|
+
this.task.options[CONTENT_EDMX] = this.hasBuildOption(CONTENT_EDMX, true) || this.hasCdsEnvOption(BUILD_NODEJS_EDMX_GENERATION, true) || this.hasBuildOption(EDMX_GENERATION, true) ? true : false
|
|
23
23
|
this.task.options[CONTENT_ENV] = this.hasBuildOption(CONTENT_ENV, true) ? true : false
|
|
24
24
|
this.task.options[CONTENT_DEFAULT_ENV_JSON] = this.hasBuildOption(CONTENT_DEFAULT_ENV_JSON, true) ? true : false
|
|
25
25
|
|
package/bin/build/util.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const cds = require('./cds')
|
|
4
|
-
const { SEVERITY_ERROR,
|
|
4
|
+
const { SEVERITY_ERROR, FILE_EXT_CDS } = require('./constants')
|
|
5
5
|
|
|
6
6
|
function getProperty(src, segments) {
|
|
7
7
|
segments = Array.isArray(segments) ? segments : segments.split('.')
|
|
@@ -126,20 +126,21 @@ function isStreamlinedMtx() {
|
|
|
126
126
|
return false
|
|
127
127
|
}
|
|
128
128
|
return (cds.env.requires.toggles
|
|
129
|
+
|| cds.env.profiles.includes('with-mtx-sidecar') // new presets
|
|
129
130
|
|| cds.env.requires['cds.xt.ModelProviderService']
|
|
130
131
|
|| (typeof cds.env.requires.multitenancy === "object")
|
|
131
132
|
) && !(() => { try { return cds.mtx } catch (e) { /**/ } })()
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
/**
|
|
135
|
-
* Returns a list of fully qualified model names belonging to the '@sap' namespace that cannot be resolved.
|
|
136
|
-
* E.g. the
|
|
137
|
-
* @param {Array} modelPaths
|
|
136
|
+
* Returns a list of fully qualified model names belonging to the '@sap' namespace that cannot be resolved.
|
|
137
|
+
* E.g. the module might NOT have been installed.
|
|
138
|
+
* @param {Array} modelPaths
|
|
138
139
|
* @returns {Array}
|
|
139
140
|
*/
|
|
140
141
|
function resolveRequiredSapModels(modelPaths) {
|
|
141
|
-
return modelPaths.filter(p => {
|
|
142
|
-
if (p.startsWith('@sap/')
|
|
142
|
+
return Array.isArray(modelPaths) && modelPaths.filter(p => {
|
|
143
|
+
if (p.startsWith('@sap/')) {
|
|
143
144
|
const files = cds.resolve(p)
|
|
144
145
|
return !files || files.length === 0
|
|
145
146
|
}
|
|
@@ -173,7 +174,7 @@ function _pushModelPaths(projectPath, ...modelPaths) {
|
|
|
173
174
|
}
|
|
174
175
|
}
|
|
175
176
|
})
|
|
176
|
-
return model
|
|
177
|
+
return [...model]
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
function flatten(modelPaths) {
|
|
@@ -203,23 +204,71 @@ async function copy(src, dest) {
|
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
/**
|
|
208
|
+
* Return gnu-style error string for location `loc`:
|
|
209
|
+
* - 'File:Line:Col' without `loc.end`
|
|
210
|
+
* - 'File:Line:StartCol-EndCol' if Line = start.line = end.line
|
|
211
|
+
* - 'File:StartLine.StartCol-EndLine.EndCol' otherwise
|
|
212
|
+
*
|
|
213
|
+
* @param {CSN.Location|CSN.Location} location
|
|
214
|
+
*/
|
|
215
|
+
function _locationString(loc) {
|
|
216
|
+
if (!loc)
|
|
217
|
+
return '<???>';
|
|
218
|
+
if (!(loc instanceof Object))
|
|
219
|
+
return loc;
|
|
220
|
+
if (!loc.line) {
|
|
221
|
+
return loc.file;
|
|
222
|
+
}
|
|
223
|
+
else if (!loc.endLine) {
|
|
224
|
+
return (loc.col)
|
|
225
|
+
? `${loc.file}:${loc.line}:${loc.col}`
|
|
226
|
+
: `${loc.file}:${loc.line}`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return (loc.line === loc.endLine)
|
|
230
|
+
? `${loc.file}:${loc.line}:${loc.col}-${loc.endCol}`
|
|
231
|
+
: `${loc.file}:${loc.line}.${loc.col}-${loc.endLine}.${loc.endCol}`;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Class for individual build message.
|
|
236
|
+
*
|
|
237
|
+
* @class BuildMessage
|
|
238
|
+
*/
|
|
239
|
+
class BuildMessage {
|
|
240
|
+
/**
|
|
241
|
+
* Creates an instance of BuildMessage.
|
|
242
|
+
* @param {string} message The message text
|
|
243
|
+
* @param {string} [severity='Error'] Severity: Debug, Info, Warning, Error
|
|
244
|
+
* @param {any} location Location of the message
|
|
245
|
+
*
|
|
246
|
+
* @memberOf BuildMessage
|
|
247
|
+
*/
|
|
248
|
+
constructor(message, severity = SEVERITY_ERROR, location) {
|
|
249
|
+
this.message = message
|
|
209
250
|
this.name = "BuildMessage"
|
|
210
251
|
this.severity = severity
|
|
211
|
-
this
|
|
252
|
+
this.$location = location
|
|
212
253
|
}
|
|
213
254
|
toString() {
|
|
214
|
-
return this
|
|
255
|
+
return `${this.$location?.file ? _locationString(this.$location) + ':' : ''} ${this.severity}: ${this.message}`
|
|
215
256
|
}
|
|
216
257
|
}
|
|
217
258
|
|
|
218
|
-
|
|
259
|
+
/**
|
|
260
|
+
* Class for combined build and compiler errors.
|
|
261
|
+
* Additional members:
|
|
262
|
+
* messages: vector of detailed build messages
|
|
263
|
+
* @class BuildError
|
|
264
|
+
* @extends {Error}
|
|
265
|
+
*/
|
|
266
|
+
class BuildError extends Error {
|
|
219
267
|
constructor(message, messages = []) {
|
|
220
|
-
super(message
|
|
268
|
+
super(message)
|
|
221
269
|
this.name = "BuildError"
|
|
222
270
|
this.messages = Array.isArray(messages) ? messages : [messages]
|
|
271
|
+
this.stack = ""
|
|
223
272
|
}
|
|
224
273
|
|
|
225
274
|
// for compatibility reasons
|
package/bin/cds-serve.js
ADDED
package/bin/cds.js
CHANGED
|
@@ -10,13 +10,20 @@ const cli = { //NOSONAR
|
|
|
10
10
|
exec (cmd = process.argv[2], ...argv) {
|
|
11
11
|
if (!argv.length) argv = process.argv.slice(3)
|
|
12
12
|
if (cmd in this.Shortcuts) cmd = process.argv[2] = this.Shortcuts[cmd]
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
if (!module.parent && ['serve', 'run'].includes(cmd)) _deprecationHint(cmd)
|
|
14
|
+
|
|
15
|
+
const task = this.load ('./'+cmd)
|
|
16
|
+
|
|
17
|
+
let args
|
|
18
|
+
try { args = task && cmd !== 'build' && this.args(task, argv) }
|
|
19
|
+
catch (err) { process.exitCode = 1; return console.error(err) }
|
|
20
|
+
|
|
21
|
+
if (task && cmd !== 'build') return task.apply (this, args)
|
|
15
22
|
|
|
16
23
|
// delegate to cds-dk for rest of commands (usually shortcuts like `cds c` including cds build if cds-dk is available)
|
|
17
24
|
const dk = _requireDk('cds')
|
|
18
25
|
if (dk) return dk.exec(cmd, ...argv)
|
|
19
|
-
if (cmd === 'build') return task.apply (this,
|
|
26
|
+
if (cmd === 'build') return task.apply (this, args)
|
|
20
27
|
_die_needsDk (cmd)
|
|
21
28
|
},
|
|
22
29
|
|
|
@@ -40,7 +47,7 @@ const cli = { //NOSONAR
|
|
|
40
47
|
else if ((k = o.indexOf(a)) >= 0) add(o[k],argv[++i])
|
|
41
48
|
else if ((k = f.indexOf(a)) >= 0) add(f[k])
|
|
42
49
|
else if (_global.test(a)) add_global(a, _flags[a] || argv[++i])
|
|
43
|
-
else throw
|
|
50
|
+
else throw 'Invalid option: '+ a
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
function add (k,v) { options[k.slice(2)] = v || true }
|
|
@@ -94,6 +101,15 @@ const _die_needsDk = (cmd) => {
|
|
|
94
101
|
return console.error (message)
|
|
95
102
|
}
|
|
96
103
|
|
|
104
|
+
function _deprecationHint(cmd) {
|
|
105
|
+
console.error(require('./utils/term').warn(`
|
|
106
|
+
Warning: \`cds ${cmd}\` will be removed with the next major version.
|
|
107
|
+
Use \`cds-serve\` instead in your start script. Set it with:
|
|
108
|
+
|
|
109
|
+
npm pkg set scripts.start="cds-serve"
|
|
110
|
+
`))
|
|
111
|
+
}
|
|
112
|
+
|
|
97
113
|
require('util').inspect.defaultOptions = {
|
|
98
114
|
colors: !!process.stdout.isTTY && !!process.stderr.isTTY,
|
|
99
115
|
depth:11, breakLength:111
|