@sap/cds 6.0.2 → 6.1.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 +153 -19
- package/apis/cds.d.ts +11 -7
- package/apis/log.d.ts +48 -0
- package/apis/ql.d.ts +72 -15
- package/bin/build/buildTaskHandler.js +5 -2
- package/bin/build/constants.js +4 -1
- package/bin/build/provider/buildTaskHandlerEdmx.js +11 -39
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +13 -32
- package/bin/build/provider/buildTaskHandlerInternal.js +56 -4
- package/bin/build/provider/buildTaskProviderInternal.js +22 -14
- package/bin/build/provider/hana/index.js +8 -7
- package/bin/build/provider/java/index.js +18 -8
- package/bin/build/provider/mtx/index.js +7 -4
- package/bin/build/provider/mtx/resourcesTarBuilder.js +64 -35
- package/bin/build/provider/mtx-extension/index.js +57 -0
- package/bin/build/provider/mtx-sidecar/index.js +46 -18
- package/bin/build/provider/nodejs/index.js +34 -13
- package/bin/build/util.js +6 -4
- package/bin/deploy/to-hana/cfUtil.js +7 -2
- package/bin/deploy/to-hana/hana.js +6 -3
- package/bin/serve.js +8 -13
- package/lib/compile/{index.js → cds-compile.js} +0 -0
- package/lib/compile/extend.js +15 -5
- package/lib/compile/minify.js +1 -15
- package/lib/compile/parse.js +1 -1
- package/lib/compile/resolve.js +2 -2
- package/lib/compile/to/srvinfo.js +6 -4
- package/lib/{deploy.js → dbs/cds-deploy.js} +8 -8
- package/lib/env/{index.js → cds-env.js} +1 -17
- package/lib/env/{requires.js → cds-requires.js} +24 -3
- package/lib/env/defaults.js +7 -1
- package/lib/env/schemas/cds-package.json +11 -0
- package/lib/env/schemas/cds-rc.json +605 -0
- package/lib/index.js +20 -17
- package/lib/log/{errors.js → cds-error.js} +1 -1
- package/lib/log/{index.js → cds-log.js} +0 -0
- package/lib/ql/SELECT.js +1 -1
- package/lib/ql/{index.js → cds-ql.js} +0 -0
- package/lib/req/cds-context.js +1 -1
- package/lib/req/context.js +35 -7
- package/lib/req/locale.js +5 -1
- package/lib/{serve → srv}/adapters.js +23 -19
- package/lib/{connect → srv}/bindings.js +0 -0
- package/lib/{connect/index.js → srv/cds-connect.js} +1 -1
- package/lib/{serve/index.js → srv/cds-serve.js} +1 -1
- package/lib/{serve → srv}/factory.js +2 -3
- package/lib/{serve/Service-api.js → srv/srv-api.js} +14 -6
- package/lib/{serve/Service-dispatch.js → srv/srv-dispatch.js} +3 -2
- package/lib/{serve/Service-handlers.js → srv/srv-handlers.js} +10 -0
- package/lib/{serve/Service-methods.js → srv/srv-methods.js} +10 -8
- package/lib/srv/srv-models.js +206 -0
- package/lib/{serve/Transaction.js → srv/srv-tx.js} +6 -1
- package/lib/utils/{tests.js → cds-test.js} +2 -2
- package/lib/utils/cds-utils.js +146 -0
- package/lib/utils/index.js +2 -136
- package/lib/utils/jest.js +43 -0
- package/lib/utils/resources/index.js +14 -24
- package/lib/utils/resources/tar.js +18 -41
- package/libx/_runtime/auth/index.js +13 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +9 -20
- package/libx/_runtime/cds-services/adapter/odata-v4/ODataRequest.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +19 -7
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +8 -11
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +6 -19
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +1 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +8 -10
- package/libx/_runtime/cds-services/adapter/odata-v4/to.js +38 -4
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +2 -6
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +8 -5
- package/libx/_runtime/cds-services/services/utils/differ.js +4 -0
- package/libx/_runtime/cds-services/util/errors.js +1 -29
- package/libx/_runtime/common/constants/events.js +1 -3
- package/libx/_runtime/common/i18n/messages.properties +2 -1
- package/libx/_runtime/common/perf/index.js +10 -15
- package/libx/_runtime/common/utils/cqn2cqn4sql.js +0 -1
- package/libx/_runtime/common/utils/entityFromCqn.js +8 -5
- package/libx/_runtime/common/utils/template.js +1 -1
- package/libx/_runtime/db/Service.js +2 -14
- package/libx/_runtime/db/expand/expandCQNToJoin.js +28 -25
- package/libx/_runtime/db/generic/input.js +4 -0
- package/libx/_runtime/db/sql-builder/SelectBuilder.js +37 -18
- package/libx/_runtime/extensibility/activate.js +47 -47
- package/libx/_runtime/extensibility/add.js +19 -13
- package/libx/_runtime/extensibility/addExtension.js +17 -13
- package/libx/_runtime/extensibility/defaults.js +25 -30
- package/libx/_runtime/extensibility/linter/allowlist_checker.js +373 -0
- package/libx/_runtime/extensibility/linter/annotations_checker.js +113 -0
- package/libx/_runtime/extensibility/linter/checker_base.js +20 -0
- package/libx/_runtime/extensibility/linter/namespace_checker.js +180 -0
- package/libx/_runtime/extensibility/linter.js +32 -0
- package/libx/_runtime/extensibility/push.js +78 -21
- package/libx/_runtime/extensibility/service.js +29 -12
- package/libx/_runtime/extensibility/token.js +56 -0
- package/libx/_runtime/extensibility/validation.js +6 -9
- package/libx/_runtime/fiori/generic/activate.js +0 -4
- package/libx/_runtime/fiori/generic/edit.js +1 -9
- package/libx/_runtime/fiori/generic/new.js +3 -28
- package/libx/_runtime/fiori/generic/patch.js +6 -7
- package/libx/_runtime/fiori/generic/prepare.js +11 -18
- package/libx/_runtime/fiori/generic/read.js +11 -1
- package/libx/_runtime/fiori/utils/handler.js +0 -17
- package/libx/_runtime/hana/Service.js +0 -1
- package/libx/_runtime/hana/conversion.js +12 -1
- package/libx/_runtime/hana/customBuilder/CustomFunctionBuilder.js +4 -3
- package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +5 -0
- package/libx/_runtime/hana/pool.js +6 -10
- package/libx/_runtime/hana/search2Contains.js +0 -5
- package/libx/_runtime/hana/search2cqn4sql.js +1 -0
- package/libx/_runtime/messaging/AMQPWebhookMessaging.js +18 -19
- package/libx/_runtime/messaging/file-based.js +1 -0
- package/libx/_runtime/messaging/outbox/utils.js +1 -1
- package/libx/_runtime/messaging/service.js +11 -6
- package/libx/_runtime/remote/utils/client.js +6 -2
- package/libx/_runtime/remote/utils/data.js +5 -0
- package/libx/_runtime/sqlite/Service.js +0 -1
- package/libx/odata/afterburner.js +79 -2
- package/libx/odata/cqn2odata.js +9 -7
- package/libx/odata/grammar.pegjs +161 -77
- package/libx/odata/index.js +9 -3
- package/libx/odata/parser.js +1 -1
- package/libx/odata/utils.js +39 -5
- package/libx/rest/RestAdapter.js +1 -2
- package/libx/rest/middleware/delete.js +4 -5
- package/libx/rest/middleware/parse.js +3 -2
- package/package.json +3 -3
- package/server.js +1 -1
- package/srv/extensibility-service.cds +6 -3
- package/srv/model-provider.cds +3 -1
- package/srv/model-provider.js +84 -104
- package/srv/mtx.js +7 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +0 -240
|
@@ -178,6 +178,10 @@ const _pickDraft = element => {
|
|
|
178
178
|
|
|
179
179
|
if (element.virtual) categories.push('virtual')
|
|
180
180
|
|
|
181
|
+
if (element.default && !DRAFT_COLUMNS_MAP[element.name]) {
|
|
182
|
+
categories.push({ category: 'default', args: element })
|
|
183
|
+
}
|
|
184
|
+
|
|
181
185
|
// REVISIT: element._foreignKeys.length seems to be a very broad check
|
|
182
186
|
if (element.isAssociation && element._foreignKeys.length) {
|
|
183
187
|
categories.push({ category: 'propagateForeignKeys' })
|
|
@@ -98,9 +98,7 @@ class SelectBuilder extends BaseBuilder {
|
|
|
98
98
|
this._orderBy(noQuoting)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
this._limit()
|
|
103
|
-
}
|
|
101
|
+
this._limit()
|
|
104
102
|
|
|
105
103
|
if (this._obj.SELECT.forUpdate) {
|
|
106
104
|
this._forUpdate()
|
|
@@ -322,11 +320,8 @@ class SelectBuilder extends BaseBuilder {
|
|
|
322
320
|
|
|
323
321
|
_where() {
|
|
324
322
|
const entityName = this._obj.SELECT.from.ref && this._obj.SELECT.from.ref[0]
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
entityName ? { ...this._options, entityName } : this._options,
|
|
328
|
-
this._csn
|
|
329
|
-
).build()
|
|
323
|
+
const options = entityName ? { ...this._options, entityName } : this._options
|
|
324
|
+
const where = new this.ExpressionBuilder(this._obj.SELECT.where, options, this._csn).build()
|
|
330
325
|
this._outputObj.sql.push('WHERE', where.sql)
|
|
331
326
|
this._outputObj.values.push(...where.values)
|
|
332
327
|
}
|
|
@@ -415,6 +410,34 @@ class SelectBuilder extends BaseBuilder {
|
|
|
415
410
|
this._outputObj.sql.push(sqls.join(', '))
|
|
416
411
|
}
|
|
417
412
|
|
|
413
|
+
_addRows() {
|
|
414
|
+
if (this._obj.SELECT.limit) {
|
|
415
|
+
if (this._obj.SELECT.limit.rows !== undefined) {
|
|
416
|
+
// limit (no placeholder for statement caching)
|
|
417
|
+
this._outputObj.sql.push('LIMIT', this._obj.SELECT.limit.rows.val)
|
|
418
|
+
} else {
|
|
419
|
+
// rows parameter is mandatory for SQL
|
|
420
|
+
throw new Error('Rows parameter is missing in SELECT.limit(rows, offset)')
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
_addOne() {
|
|
426
|
+
this._outputObj.sql.push('LIMIT', 1)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
_addOffset() {
|
|
430
|
+
// offset
|
|
431
|
+
if (this._obj.SELECT.limit && this._obj.SELECT.limit.offset !== undefined) {
|
|
432
|
+
if (typeof this._obj.SELECT.limit.offset.val === 'number' && !this._parameterizedNumbers) {
|
|
433
|
+
this._outputObj.sql.push('OFFSET', this._obj.SELECT.limit.offset.val)
|
|
434
|
+
} else {
|
|
435
|
+
this._outputObj.sql.push('OFFSET', '?')
|
|
436
|
+
this._outputObj.values.push(this._obj.SELECT.limit.offset.val)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
418
441
|
/**
|
|
419
442
|
* sql limit clause will be generated without placeholders.
|
|
420
443
|
* reason is optimizing paging queries. number of rows does not change.
|
|
@@ -423,17 +446,13 @@ class SelectBuilder extends BaseBuilder {
|
|
|
423
446
|
* offset will still use placeholders, as it'll change during the paging queries.
|
|
424
447
|
*/
|
|
425
448
|
_limit() {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (typeof this._obj.SELECT.limit.offset.val === 'number' && !this._parameterizedNumbers) {
|
|
431
|
-
this._outputObj.sql.push('OFFSET', this._obj.SELECT.limit.offset.val)
|
|
432
|
-
} else {
|
|
433
|
-
this._outputObj.sql.push('OFFSET', '?')
|
|
434
|
-
this._outputObj.values.push(this._obj.SELECT.limit.offset.val)
|
|
435
|
-
}
|
|
449
|
+
if (this._obj.SELECT.one) {
|
|
450
|
+
this._addOne()
|
|
451
|
+
} else {
|
|
452
|
+
this._addRows()
|
|
436
453
|
}
|
|
454
|
+
|
|
455
|
+
this._addOffset()
|
|
437
456
|
}
|
|
438
457
|
|
|
439
458
|
_parameters() {
|
|
@@ -1,69 +1,69 @@
|
|
|
1
1
|
const cds = require('../cds')
|
|
2
2
|
|
|
3
3
|
const handleDefaults = require('./defaults')
|
|
4
|
+
const Extensions = 'cds.xt.Extensions'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
// REVISIT: Reuse ratio = 0
|
|
7
|
+
const _calculateExtensions = async function (ID, tag) {
|
|
6
8
|
let active, inactive
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
inactiveCqn.where('ID !=', ID)
|
|
12
|
-
} else {
|
|
13
|
-
inactiveCqn.where('tag !=', tag)
|
|
14
|
-
}
|
|
15
|
-
inactive = await tx.run(inactiveCqn)
|
|
16
|
-
const activeCqn = SELECT.from('cds.xt.Extensions').where({ activated: 'database' })
|
|
17
|
-
if (ID) {
|
|
18
|
-
activeCqn.or({ ID })
|
|
19
|
-
} else {
|
|
20
|
-
if (tag) activeCqn.or({ tag })
|
|
21
|
-
}
|
|
22
|
-
active = await tx.run(activeCqn)
|
|
23
|
-
if (inactive.length) {
|
|
24
|
-
const deleteCqn = DELETE.from('cds.xt.Extensions').where(inactiveCqn.SELECT.where)
|
|
25
|
-
await tx.run(deleteCqn)
|
|
26
|
-
}
|
|
9
|
+
if (tag || ID) {
|
|
10
|
+
const inactiveCqn = SELECT.from(Extensions).where({ activated: 'propertyBag' })
|
|
11
|
+
if (ID) {
|
|
12
|
+
inactiveCqn.where('ID !=', ID)
|
|
27
13
|
} else {
|
|
28
|
-
|
|
29
|
-
inactive = []
|
|
30
|
-
active = await tx.run(SELECT.from('cds.xt.Extensions'))
|
|
14
|
+
inactiveCqn.where('(tag !=', tag, 'or tag =', null, ')')
|
|
31
15
|
}
|
|
32
|
-
|
|
16
|
+
inactive = await cds.db.run(inactiveCqn)
|
|
17
|
+
const activeCqn = SELECT.from(Extensions).where({ activated: 'database' })
|
|
18
|
+
if (ID) {
|
|
19
|
+
activeCqn.or({ ID })
|
|
20
|
+
} else if (tag) {
|
|
21
|
+
activeCqn.or({ tag })
|
|
22
|
+
}
|
|
23
|
+
active = await cds.db.run(activeCqn)
|
|
24
|
+
if (inactive.length) {
|
|
25
|
+
const deleteCqn = DELETE.from(Extensions).where(inactiveCqn.SELECT.where)
|
|
26
|
+
await cds.db.run(deleteCqn)
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
// activate all
|
|
30
|
+
inactive = []
|
|
31
|
+
active = await cds.db.run(SELECT.from(Extensions))
|
|
32
|
+
}
|
|
33
33
|
|
|
34
34
|
return { active, inactive }
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
37
|
+
// REVISIT: Reuse ratio = 0
|
|
38
|
+
const _restoreExtensions = async function (active, inactive, appCsn) {
|
|
39
|
+
// delete all extensions
|
|
40
|
+
await cds.db.run(DELETE.from(Extensions))
|
|
41
|
+
// active
|
|
42
|
+
active.forEach(row => {
|
|
43
|
+
row.csn = row.csn.replace(/,"@cds.extension":true/g, '')
|
|
44
|
+
row.activated = 'database'
|
|
45
|
+
row.timestamp = '$now'
|
|
46
|
+
})
|
|
47
|
+
await cds.db.run(INSERT.into(Extensions).entries(active))
|
|
48
|
+
// inactive
|
|
49
|
+
if (inactive.length) {
|
|
50
|
+
for (const na of inactive) {
|
|
51
|
+
for (const extension of JSON.parse(na.csn).extensions) {
|
|
52
|
+
await handleDefaults(extension, appCsn, cds.db)
|
|
54
53
|
}
|
|
55
|
-
await tx.run(INSERT.into('cds.xt.Extensions').entries(inactive))
|
|
56
54
|
}
|
|
57
|
-
|
|
55
|
+
await cds.db.run(INSERT.into(Extensions).entries(inactive))
|
|
56
|
+
}
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
// REVISIT: Review with Vitaly: (1) Delete Inactives > (2) DS.extend(t) > (3) Delete All > (4) Restore All ???
|
|
60
|
+
const activate = async function (ID, tag, tenant, appCsn) {
|
|
61
|
+
const { active, inactive } = await _calculateExtensions(ID, tag)
|
|
62
62
|
|
|
63
63
|
const { 'cds.xt.DeploymentService': ds } = cds.services
|
|
64
64
|
await ds.extend(tenant)
|
|
65
65
|
|
|
66
|
-
await _restoreExtensions(
|
|
66
|
+
await _restoreExtensions(active, inactive, appCsn)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
module.exports = activate
|
|
@@ -11,21 +11,24 @@ const add = async function (req) {
|
|
|
11
11
|
if (!extension || !extension.length) req.reject(400, 'Missing extension')
|
|
12
12
|
if (!activate) activate = 'database'
|
|
13
13
|
if (!tag) tag = null
|
|
14
|
-
const tenant =
|
|
14
|
+
const tenant = (req.user.is('internal-user') && req.data.tenant) || req.tenant
|
|
15
15
|
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
await validateExtension(csn, tenant, req)
|
|
16
|
+
const extCsn = _isCSN(extension) ? JSON.parse(extension) : cds.parse.cdl(extension)
|
|
17
|
+
if (extCsn.requires) delete extCsn.requires
|
|
19
18
|
|
|
19
|
+
const { 'cds.xt.ModelProviderService': mps } = cds.services
|
|
20
|
+
const csn = await mps.getCsn(tenant, ['*'])
|
|
21
|
+
validateExtension(extCsn, csn, req)
|
|
22
|
+
|
|
23
|
+
if (tenant) cds.context = { tenant }
|
|
20
24
|
const ID = cds.utils.uuid()
|
|
21
|
-
await cds.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
25
|
+
await cds.db.run(
|
|
26
|
+
INSERT.into('cds.xt.Extensions').entries([{ ID, tag, csn: JSON.stringify(extCsn), activated: activate }])
|
|
27
|
+
)
|
|
28
|
+
const njCsn = cds.compile.for.nodejs(csn)
|
|
29
|
+
if (activate === 'propertyBag' && extCsn.extensions)
|
|
30
|
+
extCsn.extensions.forEach(async ext => await handleDefaults(ext, njCsn))
|
|
31
|
+
if (activate === 'database') await activateExt(ID, tag, tenant, njCsn)
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
const promote = async function (req) {
|
|
@@ -35,7 +38,10 @@ const promote = async function (req) {
|
|
|
35
38
|
const tenant = req.tenant || (req.user.is('internal-user') && req.data.tenant) || ''
|
|
36
39
|
if (activate !== 'database') req.reject(400, 'Promote to propertyBag is not supported')
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
const { 'cds.xt.ModelProviderService': mps } = cds.services
|
|
42
|
+
const njCsn = await mps.getCsn(tenant, ['*'], 'nodejs')
|
|
43
|
+
if (tenant) cds.context = { tenant }
|
|
44
|
+
await activateExt(null, tag, tenant, njCsn)
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
module.exports = { add, promote }
|
|
@@ -18,9 +18,9 @@ const _addAnnotation = extension => {
|
|
|
18
18
|
})
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const _addViews = csn => {
|
|
21
|
+
const _addViews = (csn, appCsn) => {
|
|
22
22
|
csn.extensions.forEach(extension => {
|
|
23
|
-
const target =
|
|
23
|
+
const target = appCsn.definitions[extension.extend]
|
|
24
24
|
const views_ = []
|
|
25
25
|
const view = resolveViews(target, views_)
|
|
26
26
|
extension.extend = view && view.name
|
|
@@ -41,28 +41,32 @@ const _addViews = csn => {
|
|
|
41
41
|
})
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const _addExtension = async function (csn, req) {
|
|
45
|
-
|
|
46
|
-
await
|
|
44
|
+
const _addExtension = async function (csn, appCsn, req) {
|
|
45
|
+
if (req.tenant) cds.context = { tenant: req.tenant }
|
|
46
|
+
await cds.db.run(
|
|
47
47
|
INSERT.into('cds.xt.Extensions').entries([
|
|
48
48
|
{ ID: cds.utils.uuid(), tag: 'uiflex', csn: JSON.stringify(csn), activated: 'propertyBag' }
|
|
49
49
|
])
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
// defaults
|
|
53
52
|
for (const ext of req.data.extensions) {
|
|
54
53
|
const extension = JSON.parse(ext)
|
|
55
|
-
await handleDefaults(extension,
|
|
54
|
+
await handleDefaults(extension, appCsn, false)
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
const addExtension = async function (req) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
const { 'cds.xt.ModelProviderService': mps } = cds.services
|
|
60
|
+
const csn = await mps.getCsn(req.tenant, ['*'])
|
|
61
|
+
const extCsn = _getCsn(req)
|
|
62
|
+
const njCsn = cds.compile.for['nodejs'](csn)
|
|
63
|
+
// REVISIT: Optimize the validations
|
|
64
|
+
// REVISIT: Why do we need njCsn?
|
|
65
|
+
validateCsn(extCsn, njCsn, req)
|
|
66
|
+
validateExtensionFields(extCsn, njCsn, req)
|
|
67
|
+
_addViews(extCsn, njCsn)
|
|
68
|
+
validateExtension(extCsn, csn, req)
|
|
69
|
+
await _addExtension(extCsn, njCsn, req)
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
module.exports = addExtension
|
|
@@ -1,38 +1,33 @@
|
|
|
1
1
|
const cds = require('../cds')
|
|
2
|
-
|
|
3
|
-
const { ensureDraftsSuffix } = require('../common/utils/draft')
|
|
4
2
|
const resolveViews = require('./views')
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const draft = _getDraftTable(extension.extend)
|
|
20
|
-
const ext = Object.keys(extension.elements)
|
|
21
|
-
.filter(key => extension.elements[key].default)
|
|
3
|
+
const builtin = cds.builtin.types
|
|
4
|
+
const needsQuotes = Symbol()
|
|
5
|
+
builtin.string[needsQuotes] = true
|
|
6
|
+
builtin.date[needsQuotes] = true
|
|
7
|
+
|
|
8
|
+
const handleDefaults = async (extension, { definitions }, checkDb = true) => {
|
|
9
|
+
const target = definitions[extension.extend]
|
|
10
|
+
const entity = resolveViews(target)
|
|
11
|
+
if (checkDb && target !== entity) return // only db entities
|
|
12
|
+
|
|
13
|
+
const elements = extension.elements
|
|
14
|
+
const defaults = Object.keys(elements)
|
|
15
|
+
.filter(key => elements[key].default)
|
|
22
16
|
.map(key => {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
const t =
|
|
26
|
-
|
|
27
|
-
return `"${key}":${value}`
|
|
17
|
+
const e = elements[key],
|
|
18
|
+
{ val } = e.default
|
|
19
|
+
const t = definitions[e.type] || builtin[e.type]
|
|
20
|
+
return `"${key}":${t && t[needsQuotes] ? `"${val}"` : val}`
|
|
28
21
|
})
|
|
29
22
|
|
|
30
|
-
if (
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
if (defaults.length) {
|
|
24
|
+
const newDefaults = defaults.join(',')
|
|
25
|
+
const newBagpack = `extensions__ = CASE
|
|
26
|
+
WHEN extensions__ is null THEN '{${newDefaults}}'
|
|
27
|
+
ELSE '{${newDefaults},' || substr(extensions__, 2, length(extensions__)-1)
|
|
28
|
+
END`
|
|
29
|
+
await Promise.all([UPDATE(entity).with(newBagpack), entity.drafts && UPDATE(entity.drafts).with(newBagpack)])
|
|
30
|
+
// NOTE: We don't need the model definitions for `entity` in cds.db.model to run these queries
|
|
36
31
|
}
|
|
37
32
|
}
|
|
38
33
|
|