@sap/cds 6.1.0 → 6.1.3
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 +67 -0
- package/apis/log.d.ts +112 -36
- package/apis/services.d.ts +13 -2
- package/bin/build/provider/buildTaskHandlerFeatureToggles.js +2 -3
- package/bin/build/provider/hana/index.js +4 -2
- package/bin/build/provider/mtx/resourcesTarBuilder.js +4 -8
- package/bin/deploy/to-hana/hana.js +20 -25
- package/bin/deploy/to-hana/hdiDeployUtil.js +13 -2
- package/lib/dbs/cds-deploy.js +2 -2
- package/lib/env/schemas/cds-rc.json +10 -1
- package/lib/index.js +1 -1
- package/lib/log/format/kibana.js +19 -1
- package/lib/ql/Query.js +9 -3
- package/lib/ql/SELECT.js +1 -1
- package/lib/ql/UPDATE.js +2 -2
- package/lib/ql/cds-ql.js +4 -10
- package/lib/req/context.js +15 -11
- package/lib/srv/srv-api.js +8 -0
- package/lib/srv/srv-dispatch.js +11 -7
- package/lib/srv/srv-models.js +4 -3
- package/lib/srv/srv-tx.js +52 -40
- package/lib/utils/cds-utils.js +3 -3
- package/lib/utils/resources/index.js +5 -5
- package/lib/utils/resources/tar.js +1 -1
- package/libx/_runtime/auth/index.js +2 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +2 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/utils/PrimitiveValueDecoder.js +0 -2
- package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/deserializer/DeserializerFactory.js +3 -1
- package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +0 -2
- package/libx/_runtime/cds-services/services/utils/compareJson.js +3 -1
- package/libx/_runtime/cds-services/util/assert.js +3 -0
- package/libx/_runtime/common/generic/input.js +17 -2
- package/libx/_runtime/common/generic/put.js +4 -1
- package/libx/_runtime/common/generic/temporal.js +0 -3
- package/libx/_runtime/common/utils/binary.js +3 -4
- package/libx/_runtime/common/utils/keys.js +14 -6
- package/libx/_runtime/common/utils/propagateForeignKeys.js +122 -0
- package/libx/_runtime/common/utils/resolveView.js +1 -1
- package/libx/_runtime/common/utils/template.js +2 -3
- package/libx/_runtime/db/expand/expandCQNToJoin.js +1 -1
- package/libx/_runtime/db/expand/rawToExpanded.js +7 -6
- package/libx/_runtime/db/generic/input.js +7 -4
- package/libx/_runtime/db/sql-builder/InsertBuilder.js +1 -1
- package/libx/_runtime/extensibility/add.js +3 -0
- package/libx/_runtime/extensibility/handler/transformREAD.js +20 -18
- package/libx/_runtime/extensibility/push.js +11 -11
- package/libx/_runtime/extensibility/token.js +2 -1
- package/libx/_runtime/extensibility/utils.js +8 -6
- package/libx/_runtime/fiori/generic/new.js +1 -3
- package/libx/_runtime/fiori/generic/patch.js +1 -7
- package/libx/_runtime/fiori/utils/where.js +1 -1
- package/libx/_runtime/messaging/common-utils/authorizedRequest.js +1 -1
- package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +1 -2
- package/libx/_runtime/remote/utils/client.js +29 -10
- package/libx/_runtime/sqlite/Service.js +7 -5
- package/libx/_runtime/sqlite/execute.js +41 -28
- package/libx/odata/cqn2odata.js +6 -2
- package/libx/rest/RestAdapter.js +3 -6
- package/libx/rest/middleware/input.js +2 -3
- package/package.json +1 -1
- package/srv/extensibility-service.cds +4 -3
- package/srv/model-provider.js +1 -1
- package/srv/mtx.js +18 -9
- package/libx/_runtime/db/utils/propagateForeignKeys.js +0 -93
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const cds = require('../../cds')
|
|
2
|
+
|
|
3
|
+
const { prefixForStruct } = require('../../common/utils/csn')
|
|
4
|
+
|
|
5
|
+
const _autoGenerate = e => e && e.isUUID && e.key
|
|
6
|
+
|
|
7
|
+
const _set = (row, value, element, enumerable) => {
|
|
8
|
+
if (!element.parent.elements[element.name]) return // only when in model
|
|
9
|
+
if (!enumerable && element.foreignKeySource) {
|
|
10
|
+
// only for foreign keys
|
|
11
|
+
Object.defineProperty(row, element.name, {
|
|
12
|
+
get() {
|
|
13
|
+
return value
|
|
14
|
+
},
|
|
15
|
+
set(v) {
|
|
16
|
+
// Make sure that it becomes enumerable again if set manually afterwards
|
|
17
|
+
Object.defineProperty(row, element.name, { value: v, configurable: true, enumerable: true })
|
|
18
|
+
},
|
|
19
|
+
enumerable: false,
|
|
20
|
+
configurable: true
|
|
21
|
+
})
|
|
22
|
+
} else {
|
|
23
|
+
row[element.name] = value
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const _generateParentField = ({ parentElement }, row, enumerable) => {
|
|
28
|
+
if (_autoGenerate(parentElement) && !row[parentElement.name]) {
|
|
29
|
+
_set(row, cds.utils.uuid(), parentElement, enumerable)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const _generateChildField = ({ deep, childElement }, childRow, enumerable) => {
|
|
34
|
+
if (deep) {
|
|
35
|
+
_generateChildField(deep.propagation, childRow[deep.targetName], enumerable)
|
|
36
|
+
} else if (_autoGenerate(childElement) && childRow && !childRow[childElement.name]) {
|
|
37
|
+
_set(childRow, cds.utils.uuid(), childElement, enumerable)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const _getNestedVal = (row, prefix) => {
|
|
42
|
+
let val = row
|
|
43
|
+
const splitted = prefix.split('_')
|
|
44
|
+
splitted.pop() // remove last `_`
|
|
45
|
+
let k = ''
|
|
46
|
+
|
|
47
|
+
while (splitted.length > 0) {
|
|
48
|
+
k += splitted.shift()
|
|
49
|
+
if (k in val) {
|
|
50
|
+
val = val[k]
|
|
51
|
+
k = ''
|
|
52
|
+
} else {
|
|
53
|
+
k += '_'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return val
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const _propagateToChild = ({ parentElement, childElement, parentFieldValue }, row, childRow, enumerable) => {
|
|
61
|
+
if (!childElement || !childElement.parent.elements[childElement.name]) return
|
|
62
|
+
if (parentElement) {
|
|
63
|
+
const prefix = prefixForStruct(parentElement)
|
|
64
|
+
if (prefix) {
|
|
65
|
+
const nested = _getNestedVal(row, prefix)
|
|
66
|
+
_set(childRow, nested[parentElement.name], childElement, enumerable)
|
|
67
|
+
} else {
|
|
68
|
+
_set(childRow, row[parentElement.name], childElement, enumerable)
|
|
69
|
+
}
|
|
70
|
+
} else if (parentFieldValue !== undefined) {
|
|
71
|
+
_set(childRow, parentFieldValue, childElement, enumerable)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const _propagateToParent = ({ parentElement, childElement, deep }, childRow, row, enumerable) => {
|
|
76
|
+
if (deep) {
|
|
77
|
+
_propagateToParent(deep.propagation, childRow[deep.targetName], childRow, enumerable)
|
|
78
|
+
}
|
|
79
|
+
if (parentElement && childElement && childRow && childElement.name in childRow) {
|
|
80
|
+
_set(row, childRow[childElement.name], parentElement, enumerable)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = (
|
|
85
|
+
tKey,
|
|
86
|
+
row,
|
|
87
|
+
foreignKeyPropagations,
|
|
88
|
+
isCompositionEffective,
|
|
89
|
+
{ deleteAssocs = false, enumerable = true } = {}
|
|
90
|
+
) => {
|
|
91
|
+
if (!row || !(tKey in row)) return
|
|
92
|
+
if (row[tKey] === null) {
|
|
93
|
+
for (const foreignKeyPropagation of foreignKeyPropagations) {
|
|
94
|
+
if (!foreignKeyPropagation.fillChild) {
|
|
95
|
+
_set(row, null, foreignKeyPropagation.parentElement, enumerable)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (deleteAssocs && !isCompositionEffective) delete row[tKey]
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const childRows = Array.isArray(row[tKey]) ? row[tKey] : [row[tKey]]
|
|
103
|
+
|
|
104
|
+
for (const childRow of childRows) {
|
|
105
|
+
if (!childRow) return
|
|
106
|
+
|
|
107
|
+
for (const foreignKeyPropagation of foreignKeyPropagations) {
|
|
108
|
+
if (foreignKeyPropagation.fillChild) {
|
|
109
|
+
// propagate or generate in parent
|
|
110
|
+
const pk = foreignKeyPropagation.parentElement && foreignKeyPropagation.parentElement.name
|
|
111
|
+
if (pk && !(pk in row)) _propagateToParent(foreignKeyPropagation, childRow, row, enumerable)
|
|
112
|
+
if (!(pk in row)) _generateParentField(foreignKeyPropagation, row, enumerable)
|
|
113
|
+
|
|
114
|
+
if (isCompositionEffective) _propagateToChild(foreignKeyPropagation, row, childRow, enumerable)
|
|
115
|
+
} else {
|
|
116
|
+
_generateChildField(foreignKeyPropagation, childRow, enumerable)
|
|
117
|
+
_propagateToParent(foreignKeyPropagation, childRow, row, enumerable)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (deleteAssocs && !isCompositionEffective) delete row[tKey]
|
|
122
|
+
}
|
|
@@ -612,7 +612,7 @@ const _newQuery = (query, event, model, service) => {
|
|
|
612
612
|
}[event]
|
|
613
613
|
const newQuery = Object.create(query)
|
|
614
614
|
const transitions = _entityTransitionsForTarget(query[event][_prop], model, service)
|
|
615
|
-
newQuery[event] = (transitions[0] && _func(newQuery, transitions, service)) || { ...query[event] }
|
|
615
|
+
newQuery[event] = (transitions?.[0] && _func(newQuery, transitions, service)) || { ...query[event] }
|
|
616
616
|
return newQuery
|
|
617
617
|
}
|
|
618
618
|
|
|
@@ -67,7 +67,7 @@ const _getNextTarget = (model, element, currentPath = []) => {
|
|
|
67
67
|
* @param {object} targetEntity The target entity which needs to be traversed
|
|
68
68
|
* @param {object} callbacks
|
|
69
69
|
* @param {function} callbacks.pick Callback function to pick elements. If it returns a truthy value, the element will be picked. The returned value is part of the template.
|
|
70
|
-
* @param {function} callbacks.ignore Callback function to ignore
|
|
70
|
+
* @param {function} callbacks.ignore Callback function to ignore the target of an element. If it returns a truthy value, the element's target will be ignored.
|
|
71
71
|
* @param {object} [parent=null] The parent entity
|
|
72
72
|
* @param {Map} [_entityMap] This parameter is an implementation side-effect — don't use it
|
|
73
73
|
* @param {array} [targetPath=[]]
|
|
@@ -92,8 +92,6 @@ function _getTemplate(model, cache, targetEntity, callbacks, parent = null, _ent
|
|
|
92
92
|
|
|
93
93
|
for (const elementName in elements) {
|
|
94
94
|
const element = elements[elementName]
|
|
95
|
-
if (ignore && ignore(element, targetEntity, parent)) continue
|
|
96
|
-
|
|
97
95
|
_pick(pick, element, targetEntity, parent, templateElements, elementName)
|
|
98
96
|
|
|
99
97
|
if (element.items) {
|
|
@@ -101,6 +99,7 @@ function _getTemplate(model, cache, targetEntity, callbacks, parent = null, _ent
|
|
|
101
99
|
}
|
|
102
100
|
|
|
103
101
|
const { nextTargetName, nextTarget } = _getNextTarget(model, element, currentPath)
|
|
102
|
+
if (ignore && ignore(element)) continue
|
|
104
103
|
const nextTargetCached = _entityMap.get(nextTargetName)
|
|
105
104
|
|
|
106
105
|
if (nextTargetCached) {
|
|
@@ -696,7 +696,7 @@ class JoinCQNFromExpanded {
|
|
|
696
696
|
|
|
697
697
|
const assoc = entity.associations[column.ref[0]]
|
|
698
698
|
if (assoc.is2one && assoc.on) {
|
|
699
|
-
const onCond =
|
|
699
|
+
const onCond = entity._relations[assoc.name].join('target', 'source')
|
|
700
700
|
const xpr = onCond[0].xpr
|
|
701
701
|
const fks = (xpr && xpr.filter(e => e.ref && e.ref[0] === 'target').map(e => e.ref[1])) || []
|
|
702
702
|
for (const k of fks) {
|
|
@@ -30,9 +30,13 @@ class RawToExpanded {
|
|
|
30
30
|
if (each._conversionMapper) for (const [k, v] of [...each._conversionMapper]) conversionMapper.set(k, v)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
const queryResults = await Promise.all(this._queries)
|
|
34
|
+
// NOTE: this doesn't work:
|
|
35
|
+
// for (let each of this._queries) await each
|
|
36
|
+
|
|
37
|
+
for (let i = 0, length = queryResults.length; i < length; i++) {
|
|
34
38
|
const { _toManyTree: toManyTree = [] } = queries[i]
|
|
35
|
-
const result =
|
|
39
|
+
const result = queryResults[i]
|
|
36
40
|
if (toManyTree.length === 0) {
|
|
37
41
|
this._parseMainResult(result, mappings, conversionMapper, toManyTree)
|
|
38
42
|
} else {
|
|
@@ -249,10 +253,7 @@ class RawToExpanded {
|
|
|
249
253
|
* @returns {Promise<Array>} The complete expanded result set.
|
|
250
254
|
*/
|
|
251
255
|
const rawToExpanded = (configs, queries, one, rootEntity) => {
|
|
252
|
-
return new RawToExpanded(configs, queries, one, rootEntity).toExpanded()
|
|
253
|
-
Promise.all(queries).catch(() => {})
|
|
254
|
-
throw err
|
|
255
|
-
})
|
|
256
|
+
return new RawToExpanded(configs, queries, one, rootEntity).toExpanded()
|
|
256
257
|
}
|
|
257
258
|
|
|
258
259
|
module.exports = rawToExpanded
|
|
@@ -14,7 +14,7 @@ const cds = require('../../cds')
|
|
|
14
14
|
const normalizeTimeData = require('../utils/normalizeTimeData')
|
|
15
15
|
|
|
16
16
|
const { enrichDataWithKeysFromWhere } = require('../../common/utils/keys')
|
|
17
|
-
const propagateForeignKeys = require('
|
|
17
|
+
const propagateForeignKeys = require('../../common/utils/propagateForeignKeys')
|
|
18
18
|
const getTemplate = require('../../common/utils/template')
|
|
19
19
|
const templateProcessor = require('../../common/utils/templateProcessor')
|
|
20
20
|
|
|
@@ -32,8 +32,8 @@ const _processComplexCategory = ({ row, key, val, category, req, element }) => {
|
|
|
32
32
|
category = category.category
|
|
33
33
|
|
|
34
34
|
// propagate keys
|
|
35
|
-
if (category === 'propagateForeignKeys'
|
|
36
|
-
propagateForeignKeys(key, row, element._foreignKeys, element.isComposition)
|
|
35
|
+
if (category === 'propagateForeignKeys') {
|
|
36
|
+
propagateForeignKeys(key, row, element._foreignKeys, element.isComposition, { deleteAssocs: true })
|
|
37
37
|
return
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -176,7 +176,10 @@ const _pickDraft = element => {
|
|
|
176
176
|
// collect actions to apply
|
|
177
177
|
const categories = []
|
|
178
178
|
|
|
179
|
-
if (element
|
|
179
|
+
if (_isVirtualOrCalculated(element)) {
|
|
180
|
+
categories.push('virtual')
|
|
181
|
+
return { categories } // > no need to continue
|
|
182
|
+
}
|
|
180
183
|
|
|
181
184
|
if (element.default && !DRAFT_COLUMNS_MAP[element.name]) {
|
|
182
185
|
categories.push({ category: 'default', args: element })
|
|
@@ -306,7 +306,7 @@ class InsertBuilder extends BaseBuilder {
|
|
|
306
306
|
const purelyManagedColumnValues = this._getAnnotatedInsertColumnValues(annotatedColumns, purelyManagedColumns)
|
|
307
307
|
|
|
308
308
|
this._addUuidToColumns(columns, flattenColumnMap)
|
|
309
|
-
columns.push(...flattenColumnMap.keys())
|
|
309
|
+
columns.push(...Array.from(flattenColumnMap.keys()).filter(k => !columns.includes(k)))
|
|
310
310
|
|
|
311
311
|
this._addEntries(valuesArray, { columns, flattenColumnMap, purelyManagedColumnValues, insertAnnotatedColumns })
|
|
312
312
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const cds = require('../cds')
|
|
2
|
+
const LOG = cds.log('mtx')
|
|
2
3
|
|
|
3
4
|
const { validateExtension } = require('./validation')
|
|
4
5
|
const handleDefaults = require('./defaults')
|
|
@@ -16,6 +17,7 @@ const add = async function (req) {
|
|
|
16
17
|
const extCsn = _isCSN(extension) ? JSON.parse(extension) : cds.parse.cdl(extension)
|
|
17
18
|
if (extCsn.requires) delete extCsn.requires
|
|
18
19
|
|
|
20
|
+
LOG.info(`validating extension '${tag}' ...`)
|
|
19
21
|
const { 'cds.xt.ModelProviderService': mps } = cds.services
|
|
20
22
|
const csn = await mps.getCsn(tenant, ['*'])
|
|
21
23
|
validateExtension(extCsn, csn, req)
|
|
@@ -26,6 +28,7 @@ const add = async function (req) {
|
|
|
26
28
|
INSERT.into('cds.xt.Extensions').entries([{ ID, tag, csn: JSON.stringify(extCsn), activated: activate }])
|
|
27
29
|
)
|
|
28
30
|
const njCsn = cds.compile.for.nodejs(csn)
|
|
31
|
+
LOG.info(`activating extension to '${activate}' ...`)
|
|
29
32
|
if (activate === 'propertyBag' && extCsn.extensions)
|
|
30
33
|
extCsn.extensions.forEach(async ext => await handleDefaults(ext, njCsn))
|
|
31
34
|
if (activate === 'database') await activateExt(ID, tag, tenant, njCsn)
|
|
@@ -42,23 +42,25 @@ const _removeExtendedFields = (columns, extFields, alias) => {
|
|
|
42
42
|
|
|
43
43
|
const _transformUnion = (req, model) => {
|
|
44
44
|
// second element is active entity
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
if (req.target) {
|
|
46
|
+
const name = req.target.name.SET ? req.target.name.SET.args[1]._target.name : req.target.name
|
|
47
|
+
const extFields = getExtendedFields(name, model)
|
|
48
|
+
_addBackPack(req.query.SELECT.columns, extFields)
|
|
49
|
+
_removeExtendedFields(req.query.SELECT.columns, extFields)
|
|
50
|
+
|
|
51
|
+
_addBackPack(
|
|
52
|
+
req.query.SELECT.from.SET.args[0].SELECT.columns,
|
|
53
|
+
extFields,
|
|
54
|
+
req.query.SELECT.from.SET.args[0].SELECT.from.args[0].as
|
|
55
|
+
)
|
|
56
|
+
_addBackPack(req.query.SELECT.from.SET.args[1].SELECT.columns, extFields)
|
|
57
|
+
_removeExtendedFields(
|
|
58
|
+
req.query.SELECT.from.SET.args[0].SELECT.columns,
|
|
59
|
+
extFields,
|
|
60
|
+
req.query.SELECT.from.SET.args[0].SELECT.from.args[0].as
|
|
61
|
+
)
|
|
62
|
+
_removeExtendedFields(req.query.SELECT.from.SET.args[1].SELECT.columns, extFields)
|
|
63
|
+
}
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
const _getAliasedEntitiesForJoin = (args, model) => {
|
|
@@ -111,7 +113,7 @@ function transformExtendedFieldsREAD(req) {
|
|
|
111
113
|
_transformColumns(req.query.SELECT.columns, target.name, this.model)
|
|
112
114
|
|
|
113
115
|
if (req.query.SELECT.from.SET) return _transformUnion(req, this.model) // union
|
|
114
|
-
if (req.query.SELECT.from.join) return _transformJoin(req, this.model) // join
|
|
116
|
+
if (req.query.SELECT.from.join && req.query.SELECT.from.args) return _transformJoin(req, this.model) // join
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
module.exports = {
|
|
@@ -22,7 +22,7 @@ const _compileProject = async function (extension, req) {
|
|
|
22
22
|
if (err.messages) req.reject(400, getCompilerError(err.messages))
|
|
23
23
|
else throw err
|
|
24
24
|
} finally {
|
|
25
|
-
|
|
25
|
+
fs.promises.rm(root, { recursive: true, force: true }).catch(() => {})
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
return { csn, files }
|
|
@@ -68,7 +68,7 @@ const pull = async function (req) {
|
|
|
68
68
|
// for (const file of i18nFiles) await _copyFile(file, temp)
|
|
69
69
|
tgz = await packTarArchive(temp)
|
|
70
70
|
} finally {
|
|
71
|
-
|
|
71
|
+
fs.promises.rm(temp, { recursive: true, force: true }).catch(() => {})
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
return tgz
|
|
@@ -85,31 +85,31 @@ const push = async function (req) {
|
|
|
85
85
|
if (tenant) cds.context = { tenant }
|
|
86
86
|
|
|
87
87
|
// remove current extension with tag
|
|
88
|
-
let currentExt
|
|
89
88
|
if (tag) {
|
|
90
|
-
|
|
91
|
-
if (currentExt.length) await cds.db.run(DELETE.from('cds.xt.Extensions').where({ tag }))
|
|
89
|
+
await DELETE.from('cds.xt.Extensions').where({ tag })
|
|
92
90
|
}
|
|
93
91
|
|
|
94
92
|
LOG.info(`validating extension '${tag}' ...`)
|
|
95
93
|
// validation
|
|
96
94
|
const { 'cds.xt.ModelProviderService': mps } = cds.services
|
|
95
|
+
// REVISIT: Isn't that also done during activate?
|
|
97
96
|
const csn = await mps.getCsn(tenant, Object.keys(cds.context.features || {}))
|
|
98
97
|
try {
|
|
99
98
|
cds.extend(csn).with(extCsn)
|
|
100
99
|
} catch (err) {
|
|
101
|
-
if (currentExt && currentExt.length) {
|
|
102
|
-
await cds.db.run(INSERT.into('cds.xt.Extensions').entries(currentExt)) // REVISIT: why did we eagerly delete that at all above?
|
|
103
|
-
}
|
|
104
100
|
return req.reject(400, getCompilerError(err.messages))
|
|
105
101
|
}
|
|
106
102
|
await linter(extCsn, csn, files, req)
|
|
107
103
|
|
|
108
104
|
// insert and activate extension
|
|
109
105
|
const ID = cds.utils.uuid()
|
|
110
|
-
await cds.
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
await INSERT.into('cds.xt.Extensions').entries({
|
|
107
|
+
ID,
|
|
108
|
+
csn: JSON.stringify(extCsn),
|
|
109
|
+
sources,
|
|
110
|
+
activated: 'database',
|
|
111
|
+
tag
|
|
112
|
+
})
|
|
113
113
|
|
|
114
114
|
LOG.info(`activating extension '${tag}' ...`)
|
|
115
115
|
await activate(ID, null, tenant)
|
|
@@ -43,7 +43,8 @@ module.exports = {
|
|
|
43
43
|
)
|
|
44
44
|
response.send(data)
|
|
45
45
|
} catch (error) {
|
|
46
|
-
|
|
46
|
+
const rootCause = error.response?.data ? JSON.stringify(error.response?.data) : error.message
|
|
47
|
+
error.message = `Authentication failed with root cause '${rootCause}'. Passcode URL: https://${parsedUrl.hostname}/passcode`
|
|
47
48
|
const {
|
|
48
49
|
constructor: { name },
|
|
49
50
|
message
|
|
@@ -8,7 +8,7 @@ const EXT_BACK_PACK = 'extensions__'
|
|
|
8
8
|
|
|
9
9
|
const getTargetRead = req => {
|
|
10
10
|
let name = ''
|
|
11
|
-
if (req.query.SELECT.from.join) {
|
|
11
|
+
if (req.query.SELECT.from.join && req.query.SELECT.from.args) {
|
|
12
12
|
// join
|
|
13
13
|
name = req.query.SELECT.from.args.find(arg => arg.ref && arg.ref[0] !== 'DRAFT.DraftAdministativeData').ref[0]
|
|
14
14
|
} else if (req.target.name.SET) {
|
|
@@ -63,15 +63,17 @@ const hasExtendedEntity = (req, model) => {
|
|
|
63
63
|
return true
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
if (req.query.SELECT.from.join) {
|
|
66
|
+
if (req.query.SELECT.from.join && req.query.SELECT.from.args) {
|
|
67
67
|
return _hasExtendedEntityArgs(req.query.SELECT.from.args, model)
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
if (req.target
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
if (req.target) {
|
|
71
|
+
if (req.target.name.SET) {
|
|
72
|
+
return isExtendedEntity(req.target.name.SET.args[0]._target.name, model)
|
|
73
|
+
}
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
return isExtendedEntity(req.target.name, model)
|
|
76
|
+
}
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
const getExtendedFields = (entityName, model) => {
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
|
-
const { INSERT,
|
|
2
|
+
const { INSERT, UPDATE } = cds.ql
|
|
3
3
|
|
|
4
4
|
const onDraftActivate = require('./activate')._handler
|
|
5
5
|
const { isNavigationToMany } = require('../utils/req')
|
|
6
|
-
const { getKeysCondition } = require('../utils/where')
|
|
7
6
|
const { ensureDraftsSuffix } = require('../utils/handler')
|
|
8
|
-
const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
|
|
9
7
|
|
|
10
8
|
const _getUpdateDraftAdminCQN = ({ user, timestamp }, draftUUID) => {
|
|
11
9
|
return UPDATE('DRAFT.DraftAdministrativeData')
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
const cds = require('../../cds')
|
|
2
2
|
const { UPDATE, SELECT } = cds.ql
|
|
3
3
|
|
|
4
|
-
const {
|
|
5
|
-
getUpdateDraftAdminCQN,
|
|
6
|
-
removeDraftUUIDIfNecessary,
|
|
7
|
-
ensureDraftsSuffix,
|
|
8
|
-
ensureNoDraftsSuffix,
|
|
9
|
-
addColumnAlias
|
|
10
|
-
} = require('../utils/handler')
|
|
4
|
+
const { getUpdateDraftAdminCQN, ensureDraftsSuffix, ensureNoDraftsSuffix, addColumnAlias } = require('../utils/handler')
|
|
11
5
|
const { getKeysCondition } = require('../utils/where')
|
|
12
6
|
const { getColumns } = require('../../cds-services/services/utils/columns')
|
|
13
7
|
const { DRAFT_COLUMNS_CQN } = require('../../common/constants/draft')
|
|
@@ -160,7 +160,7 @@ const isActiveEntityRequested = where => {
|
|
|
160
160
|
|
|
161
161
|
while (where[i]) {
|
|
162
162
|
if (where[i].xpr) {
|
|
163
|
-
const isRequested = isActiveEntityRequested(where.xpr)
|
|
163
|
+
const isRequested = isActiveEntityRequested(where[i].xpr)
|
|
164
164
|
if (isRequested) return true
|
|
165
165
|
}
|
|
166
166
|
if (
|
|
@@ -19,7 +19,7 @@ const authorizedRequest = ({ method, uri, path, oa2, tenant, dataObj, headers, t
|
|
|
19
19
|
if (dataObj) {
|
|
20
20
|
data = JSON.stringify(dataObj)
|
|
21
21
|
httpOptions.headers['Content-Type'] = 'application/json'
|
|
22
|
-
httpOptions.headers['Content-Length'] = data
|
|
22
|
+
httpOptions.headers['Content-Length'] = Buffer.byteLength(data)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
if (headers) {
|
|
@@ -34,7 +34,6 @@ class EMManagement {
|
|
|
34
34
|
this.namespace = namespace
|
|
35
35
|
this.LOG = LOG
|
|
36
36
|
}
|
|
37
|
-
|
|
38
37
|
async getQueue(queueName = this.queueName) {
|
|
39
38
|
this.LOG._info &&
|
|
40
39
|
this.LOG.info(
|
|
@@ -303,7 +302,7 @@ class EMManagement {
|
|
|
303
302
|
grantType: 'client_credentials',
|
|
304
303
|
clientId: this.optionsMessagingREST.oa2.client,
|
|
305
304
|
clientSecret: this.optionsMessagingREST.oa2.secret,
|
|
306
|
-
tokenUrl: this.optionsMessagingREST.oa2.endpoint
|
|
305
|
+
tokenUrl: this.optionsMessagingREST.oa2.endpoint // this is the changed tokenUrl
|
|
307
306
|
}
|
|
308
307
|
}
|
|
309
308
|
|
|
@@ -24,8 +24,8 @@ const _sanitizeHeaders = headers => {
|
|
|
24
24
|
|
|
25
25
|
const _executeHttpRequest = async ({ requestConfig, destination, destinationOptions, jwt }) => {
|
|
26
26
|
const { executeHttpRequestWithOrigin } = cloudSdk()
|
|
27
|
-
|
|
28
27
|
const destinationName = typeof destination === 'string' && destination
|
|
28
|
+
|
|
29
29
|
if (destinationName) {
|
|
30
30
|
destination = { destinationName, ...(resolveDestinationOptions(destinationOptions, jwt) || {}) }
|
|
31
31
|
} else if (destination.forwardAuthToken) {
|
|
@@ -59,7 +59,13 @@ const _executeHttpRequest = async ({ requestConfig, destination, destinationOpti
|
|
|
59
59
|
|
|
60
60
|
// cloud sdk requires a new mechanism to differentiate the priority of headers
|
|
61
61
|
// "custom" keeps the highest priority as before
|
|
62
|
-
requestConfig = {
|
|
62
|
+
requestConfig = {
|
|
63
|
+
...(cds.env?.remote?.max_body_length && { maxBodyLength: cds.env.remote.max_body_length }),
|
|
64
|
+
...requestConfig,
|
|
65
|
+
headers: {
|
|
66
|
+
custom: { ...requestConfig.headers }
|
|
67
|
+
}
|
|
68
|
+
}
|
|
63
69
|
|
|
64
70
|
return executeHttpRequestWithOrigin(destination, requestConfig, requestOptions)
|
|
65
71
|
}
|
|
@@ -145,10 +151,12 @@ function _defineProperty(obj, property, value) {
|
|
|
145
151
|
const map = (..._) => _defineProperty(_map.call(obj, ..._), property, value)
|
|
146
152
|
props.map = { value: map, enumerable: false, configurable: true, writable: true }
|
|
147
153
|
}
|
|
154
|
+
|
|
148
155
|
props[property] = { value: value, enumerable: false, configurable: true, writable: true }
|
|
149
156
|
for (const prop in props) {
|
|
150
157
|
Object.defineProperty(obj, prop, props[prop])
|
|
151
158
|
}
|
|
159
|
+
|
|
152
160
|
return obj
|
|
153
161
|
}
|
|
154
162
|
|
|
@@ -156,14 +164,17 @@ function _normalizeMetadata(prefix, data, results) {
|
|
|
156
164
|
const target = results !== undefined ? results : data
|
|
157
165
|
if (typeof target !== 'object' || target === null) return target
|
|
158
166
|
const metadataKeys = Object.keys(data).filter(k => prefix.test(k))
|
|
167
|
+
|
|
159
168
|
for (const k of metadataKeys) {
|
|
160
169
|
const $ = k.replace(prefix, '$')
|
|
161
170
|
_defineProperty(target, $, data[k])
|
|
162
171
|
delete target[k]
|
|
163
172
|
}
|
|
173
|
+
|
|
164
174
|
if (Array.isArray(target)) {
|
|
165
175
|
return target.map(row => _normalizeMetadata(prefix, row))
|
|
166
176
|
}
|
|
177
|
+
|
|
167
178
|
// check properties for all and prop.results for odata v2
|
|
168
179
|
for (const [key, value] of Object.entries(target)) {
|
|
169
180
|
if (value && typeof value === 'object') {
|
|
@@ -171,6 +182,7 @@ function _normalizeMetadata(prefix, data, results) {
|
|
|
171
182
|
target[key] = _normalizeMetadata(prefix, value, nestedResults)
|
|
172
183
|
}
|
|
173
184
|
}
|
|
185
|
+
|
|
174
186
|
return target
|
|
175
187
|
}
|
|
176
188
|
const _getPurgedRespActionFunc = (data, returnType) => {
|
|
@@ -181,6 +193,7 @@ const _getPurgedRespActionFunc = (data, returnType) => {
|
|
|
181
193
|
return data[key]
|
|
182
194
|
}
|
|
183
195
|
}
|
|
196
|
+
|
|
184
197
|
return data
|
|
185
198
|
}
|
|
186
199
|
|
|
@@ -198,6 +211,7 @@ const _purgeODataV2 = (data, target, returnType, reqHeaders) => {
|
|
|
198
211
|
ieee754Compatible,
|
|
199
212
|
exponentialDecimals
|
|
200
213
|
)
|
|
214
|
+
|
|
201
215
|
return _normalizeMetadata(/^__/, data, convertedResponse)
|
|
202
216
|
}
|
|
203
217
|
|
|
@@ -217,6 +231,7 @@ const _getSanitizedError = (e, reqOptions, options = { suppressRemoteResponseBod
|
|
|
217
231
|
url: e.config ? e.config.baseURL + e.config.url : reqOptions.url,
|
|
218
232
|
headers: e.config ? e.config.headers : reqOptions.headers
|
|
219
233
|
}
|
|
234
|
+
|
|
220
235
|
if (options.batchRequest) {
|
|
221
236
|
e.request.body = reqOptions.data
|
|
222
237
|
}
|
|
@@ -227,9 +242,11 @@ const _getSanitizedError = (e, reqOptions, options = { suppressRemoteResponseBod
|
|
|
227
242
|
statusText: e.response.statusText,
|
|
228
243
|
headers: e.response.headers
|
|
229
244
|
}
|
|
245
|
+
|
|
230
246
|
if (e.response.data && !options.suppressRemoteResponseBody) {
|
|
231
247
|
response.body = e.response.data
|
|
232
248
|
}
|
|
249
|
+
|
|
233
250
|
e.response = response
|
|
234
251
|
}
|
|
235
252
|
|
|
@@ -270,12 +287,7 @@ const run = async (
|
|
|
270
287
|
response = await _executeHttpRequest({ requestConfig, destination, destinationOptions, jwt })
|
|
271
288
|
} catch (e) {
|
|
272
289
|
// > axios received status >= 400 -> gateway error
|
|
273
|
-
const msg =
|
|
274
|
-
(e.response &&
|
|
275
|
-
e.response.data &&
|
|
276
|
-
e.response.data.error &&
|
|
277
|
-
((e.response.data.error.message && e.response.data.error.message.value) || e.response.data.error.message)) ||
|
|
278
|
-
e.message
|
|
290
|
+
const msg = e?.response?.data?.error?.message?.value ?? e?.response?.data?.error?.message ?? e.message
|
|
279
291
|
e.message = msg ? 'Error during request to remote service: \n' + msg : 'Request to remote service failed.'
|
|
280
292
|
|
|
281
293
|
const sanitizedError = _getSanitizedError(e, requestConfig, {
|
|
@@ -284,7 +296,6 @@ const run = async (
|
|
|
284
296
|
|
|
285
297
|
const err = Object.assign(new Error(e.message), { statusCode: 502, reason: sanitizedError })
|
|
286
298
|
LOG._warn && LOG.warn(err)
|
|
287
|
-
|
|
288
299
|
throw err
|
|
289
300
|
}
|
|
290
301
|
|
|
@@ -312,7 +323,6 @@ const run = async (
|
|
|
312
323
|
})
|
|
313
324
|
|
|
314
325
|
LOG._warn && LOG.warn(err)
|
|
315
|
-
|
|
316
326
|
throw err
|
|
317
327
|
}
|
|
318
328
|
|
|
@@ -331,6 +341,7 @@ const run = async (
|
|
|
331
341
|
if (responseDataSplitted[1].startsWith('HTTP/1.1 2')) {
|
|
332
342
|
response.data = contentJSON
|
|
333
343
|
}
|
|
344
|
+
|
|
334
345
|
if (responseDataSplitted[1].startsWith('HTTP/1.1 4') || responseDataSplitted[1].startsWith('HTTP/1.1 5')) {
|
|
335
346
|
const innerError = contentJSON.error || contentJSON
|
|
336
347
|
innerError.status = Number(responseDataSplitted[1].match(/HTTP.*(\d{3})/m)[1])
|
|
@@ -357,6 +368,7 @@ const run = async (
|
|
|
357
368
|
}
|
|
358
369
|
return _purgeODataV4(response.data)
|
|
359
370
|
}
|
|
371
|
+
|
|
360
372
|
return response.data
|
|
361
373
|
}
|
|
362
374
|
|
|
@@ -368,6 +380,7 @@ const getJwt = req => {
|
|
|
368
380
|
return token[1]
|
|
369
381
|
}
|
|
370
382
|
}
|
|
383
|
+
|
|
371
384
|
return null
|
|
372
385
|
}
|
|
373
386
|
|
|
@@ -382,9 +395,11 @@ const _cqnToReqOptions = (query, service, req) => {
|
|
|
382
395
|
.replace(/\( /g, '(')
|
|
383
396
|
.replace(/ \)/g, ')')
|
|
384
397
|
}
|
|
398
|
+
|
|
385
399
|
if (queryObject.method !== 'GET' && queryObject.method !== 'HEAD') {
|
|
386
400
|
reqOptions.data = kind === 'odata-v2' ? convertV2PayloadData(queryObject.body, req.target) : queryObject.body
|
|
387
401
|
}
|
|
402
|
+
|
|
388
403
|
return reqOptions
|
|
389
404
|
}
|
|
390
405
|
|
|
@@ -395,9 +410,11 @@ const _stringToReqOptions = (query, data, target) => {
|
|
|
395
410
|
method: cleanQuery.substring(0, blankIndex).toUpperCase(),
|
|
396
411
|
url: encodeURI(formatPath(cleanQuery.substring(blankIndex, cleanQuery.length).trim()))
|
|
397
412
|
}
|
|
413
|
+
|
|
398
414
|
if (data && reqOptions.method !== 'GET' && reqOptions.method !== 'HEAD') {
|
|
399
415
|
reqOptions.data = this.kind === 'odata-v2' ? Object.assign({}, convertV2PayloadData(data, target)) : data
|
|
400
416
|
}
|
|
417
|
+
|
|
401
418
|
return reqOptions
|
|
402
419
|
}
|
|
403
420
|
|
|
@@ -412,10 +429,12 @@ const _pathToReqOptions = (method, path, data, target) => {
|
|
|
412
429
|
// normalize in case parts[2] already starts with /
|
|
413
430
|
url = url.replace(/^\/\//, '/')
|
|
414
431
|
}
|
|
432
|
+
|
|
415
433
|
const reqOptions = { method, url }
|
|
416
434
|
if (data && reqOptions.method !== 'GET' && reqOptions.method !== 'HEAD') {
|
|
417
435
|
reqOptions.data = this.kind === 'odata-v2' ? Object.assign({}, convertV2PayloadData(data, target)) : data
|
|
418
436
|
}
|
|
437
|
+
|
|
419
438
|
return reqOptions
|
|
420
439
|
}
|
|
421
440
|
|