@sap/cds-dk 7.1.1 → 7.3.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/activate.js +2 -2
- package/bin/build.js +5 -2
- package/bin/compile/index.js +13 -5
- package/bin/env.js +0 -3
- package/bin/extend.js +25 -3
- package/bin/gen.js +27 -0
- package/bin/help.js +2 -1
- package/bin/import.js +7 -3
- package/bin/push.js +10 -6
- package/bin/watch.js +3 -4
- package/bin/watched.js +2 -3
- package/lib/bind/bindingUtil.js +1 -0
- package/lib/build/{buildTaskHandler.js → buildPlugin.js} +25 -30
- package/lib/build/buildTaskEngine.js +84 -89
- package/lib/build/buildTaskFactory.js +23 -44
- package/lib/build/buildTaskProvider.js +24 -19
- package/lib/build/buildTaskProviderFactory.js +176 -66
- package/lib/build/constants.js +6 -5
- package/lib/build/index.js +4 -3
- package/lib/build/provider/{buildTaskHandlerEdmx.js → edmxBuildPlugin.js} +6 -6
- package/lib/build/provider/{buildTaskHandlerFeatureToggles.js → featureToggleBuildPlugin.js} +3 -3
- package/lib/build/provider/fiori/index.js +5 -6
- package/lib/build/provider/hana/index.js +67 -36
- package/lib/build/provider/{buildTaskHandlerInternal.js → internalBuildPlugin.js} +11 -11
- package/lib/build/provider/{buildTaskProviderInternal.js → internalBuildTaskProvider.js} +42 -53
- package/lib/build/provider/java/index.js +24 -24
- package/lib/build/provider/mtx/index.js +4 -4
- package/lib/build/provider/mtx/resourcesTarBuilder.js +8 -8
- package/lib/build/provider/mtx-extension/index.js +17 -9
- package/lib/build/provider/mtx-sidecar/index.js +17 -11
- package/lib/build/provider/nodejs/index.js +63 -29
- package/lib/build/util.js +11 -19
- package/lib/client/auth_manager.js +6 -12
- package/lib/client/base_command.js +1 -1
- package/lib/client/download_legacy.js +48 -0
- package/lib/client/params.js +5 -0
- package/lib/client/push.js +41 -1
- package/lib/client/settings_manager.js +12 -3
- package/lib/compile/openapi/csdl2openapi.js +30 -21
- package/lib/compile/openapi/index.js +7 -3
- package/lib/env/schema.js +36 -0
- package/lib/env/schemas/cds-rc.json +101 -0
- package/lib/import/common.js +24 -11
- package/lib/import/importapi.js +2 -2
- package/lib/import/message.js +3 -1
- package/lib/import/odata/v4parser.js +23 -7
- package/lib/index.js +5 -0
- package/lib/init/constants.js +9 -12
- package/lib/init/index.js +86 -74
- package/lib/init/template/_merging/registry-mta.js +21 -12
- package/lib/init/template/approuter/files/mta.yaml.hbs +1 -1
- package/lib/init/template/approuter/index.js +3 -2
- 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/connectivity/index.js +5 -4
- package/lib/init/template/destination/index.js +2 -5
- 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/html5-repo/index.js +27 -3
- package/lib/init/template/lint/index.js +3 -7
- package/lib/init/template/mta/index.js +9 -3
- 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/postgres/files/mta.yaml.hbs +27 -0
- package/lib/init/template/postgres/files/package.json.hbs +5 -0
- package/lib/init/template/postgres/index.js +52 -0
- 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 +2 -7
- package/lib/init/util/template.js +7 -6
- package/lib/util/pruneAxiosErrors.js +22 -8
- package/lib/watch/node-watch.js +6 -1
- package/npm-shrinkwrap.json +2207 -384
- package/package.json +3 -3
- 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
|
@@ -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))
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const { join } = require('path')
|
|
2
|
+
const { mergeJSON } = require('../init/util/merge')
|
|
3
|
+
const { readJSON } = require('../util/fs')
|
|
4
|
+
|
|
5
|
+
module.exports = async function overlay4(schemaName, cwd = process.cwd()) {
|
|
6
|
+
|
|
7
|
+
// need to load @sap/cds from current cwd which might
|
|
8
|
+
// differ from initial cwd to get project specific cds version
|
|
9
|
+
const cds = require(require.resolve('@sap/cds', {
|
|
10
|
+
paths: [cwd, __dirname]
|
|
11
|
+
}))
|
|
12
|
+
|
|
13
|
+
// force cds.root to be project root
|
|
14
|
+
cds.root = cwd
|
|
15
|
+
|
|
16
|
+
// load defaults, throw err if schema not found
|
|
17
|
+
let result;
|
|
18
|
+
if (typeof cds.schema?.default4 === 'function') {
|
|
19
|
+
result = await cds.schema.default4(schemaName)
|
|
20
|
+
} else if (cds.env?.schemas?.[schemaName]) {
|
|
21
|
+
// backwards compatibility
|
|
22
|
+
result = await readJSON(cds.env.schemas[schemaName])
|
|
23
|
+
} else {
|
|
24
|
+
throw new Error(`cds ${cds.version} in ${cwd} does not support schema retrieval.`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// optional, don't throw if file not found
|
|
28
|
+
try {
|
|
29
|
+
await mergeJSON(result, join(__dirname, 'schemas', schemaName))
|
|
30
|
+
result.description = result.description + ' - overlaid'
|
|
31
|
+
} catch (err) {
|
|
32
|
+
if (err.code !== 'ENOENT') throw err
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return result
|
|
36
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "JSON schema for CDS-DK configuration",
|
|
3
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
|
+
"description": "This is a JSON schema representation for CDS project configuration",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": true,
|
|
7
|
+
"properties": {
|
|
8
|
+
"build": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"description": "Customize CDS build settings.",
|
|
11
|
+
"additionalProperties": true,
|
|
12
|
+
"properties": {
|
|
13
|
+
"target": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Specify the folder which contains generated build results. Defaults to './gen'.",
|
|
16
|
+
"format": "uri-reference"
|
|
17
|
+
},
|
|
18
|
+
"tasks": {
|
|
19
|
+
"type": "array",
|
|
20
|
+
"description": "Control which tasks 'cds build' will execute on your project modules. This is a typical task configuration: '\"tasks\": [{ \"for\": \"hana\"}];",
|
|
21
|
+
"uniqueItems": true,
|
|
22
|
+
"default": [
|
|
23
|
+
{}
|
|
24
|
+
],
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": true,
|
|
28
|
+
"properties": {
|
|
29
|
+
"dest": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Optional: Define the destination folder for this build task, relative to build target folder. \nFor example: Destination defined as 'custom' results in an output written to 'gen/custom'.",
|
|
32
|
+
"format": "uri-reference"
|
|
33
|
+
},
|
|
34
|
+
"src": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "Optional: Prefer defining 'cds.folders'. Use 'src' if you want it only to be valid for this specific build task, which excludes, for example, 'cds watch'.",
|
|
37
|
+
"format": "uri-reference"
|
|
38
|
+
},
|
|
39
|
+
"for": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "Define the target technology for this build task.",
|
|
42
|
+
"minLength": 1,
|
|
43
|
+
"enum": [
|
|
44
|
+
"nodejs",
|
|
45
|
+
"java",
|
|
46
|
+
"hana",
|
|
47
|
+
"mtx-sidecar",
|
|
48
|
+
"mtx-extension",
|
|
49
|
+
"mtx",
|
|
50
|
+
"fiori",
|
|
51
|
+
"node-cf",
|
|
52
|
+
"java-cf"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"options": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"description": "Optional: Set target technology specific options for this build task.",
|
|
58
|
+
"additionalProperties": true,
|
|
59
|
+
"properties": {
|
|
60
|
+
"model": {
|
|
61
|
+
"$ref": "#/$defs/foldersStringArray"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"deploy": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"description": "Customize 'cds deploy' settings.",
|
|
73
|
+
"additionalProperties": true,
|
|
74
|
+
"properties": {
|
|
75
|
+
"no_save": {
|
|
76
|
+
"type": "boolean",
|
|
77
|
+
"description": "Do / don't update configuration in package.json.",
|
|
78
|
+
"default": true
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"$defs": {
|
|
84
|
+
"foldersStringArray": {
|
|
85
|
+
"oneOf": [
|
|
86
|
+
{
|
|
87
|
+
"type": "string",
|
|
88
|
+
"format": "uri-reference"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"type": "array",
|
|
92
|
+
"uniqueItems": true,
|
|
93
|
+
"items": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"format": "uri-reference"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
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',
|
|
@@ -17,7 +17,7 @@ let optionsMapping = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
let apiOptions = ['includeNamespaces', 'keepNamespace'];
|
|
20
|
-
let commonOptions = ['no_copy', 'no_save', 'out', 'dry', 'as', 'from', 'force'];
|
|
20
|
+
let commonOptions = ['no_copy', 'no_save', 'out', 'dry', 'as', 'from', 'force', 'config'];
|
|
21
21
|
|
|
22
22
|
async function generateEDMX2JSON(edmx) {
|
|
23
23
|
let cjson = convert.xml2json(edmx, {
|
|
@@ -148,6 +148,18 @@ async function _copyToSrvExternal(file, dst, cwd) {
|
|
|
148
148
|
return file;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
async function isValidJsonObject(options){
|
|
152
|
+
let config;
|
|
153
|
+
try {
|
|
154
|
+
if (options.config) {
|
|
155
|
+
config = JSON.parse(options.config);
|
|
156
|
+
return config;
|
|
157
|
+
}
|
|
158
|
+
} catch (error) {
|
|
159
|
+
throw new Error(messages.SPECIFY_CONFIG);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
151
163
|
async function preProcess(file, options, cwd) {
|
|
152
164
|
let srcFilePath = await isValidInputFile(path.resolve(cwd, file));
|
|
153
165
|
if (options.as && !_supportedAsFormats().includes(options.as))
|
|
@@ -266,12 +278,12 @@ async function _generateChecksumValidate(dest, output, force) {
|
|
|
266
278
|
return "/* checksum : " + currentChecksum + " */\n" + output;
|
|
267
279
|
}
|
|
268
280
|
|
|
269
|
-
async function _getResult(output, as, dest, force,
|
|
281
|
+
async function _getResult(output, as, dest, force, kind) {
|
|
270
282
|
let extension = dest.substring(dest.lastIndexOf(".") + 1);
|
|
271
283
|
as = (as === "cds") ? "cdl" : as;
|
|
272
284
|
|
|
273
285
|
// output filepath should simply contain .csn as extension
|
|
274
|
-
if (
|
|
286
|
+
if (kind === 'rest' && ['swagger', 'openapi3'].includes(extension)) {
|
|
275
287
|
dest = dest.replace('.' + extension, '');
|
|
276
288
|
}
|
|
277
289
|
|
|
@@ -325,19 +337,19 @@ ${using}
|
|
|
325
337
|
console.log(message);
|
|
326
338
|
}
|
|
327
339
|
|
|
328
|
-
async function _updatePackageJson(dest, services,
|
|
340
|
+
async function _updatePackageJson(dest, services, kind, cwd, config) {
|
|
329
341
|
const package_json = path.resolve(cwd, 'package.json');
|
|
330
342
|
const conf = await exists(package_json) ? require(package_json) : {};
|
|
331
343
|
const requires = ['cds', 'requires'].reduce((p, n) => p[n] || (p[n] = {}), conf);
|
|
332
|
-
if (
|
|
333
|
-
|
|
344
|
+
if (kind !== 'rest') {
|
|
345
|
+
kind = (kind === "V2") ? 'odata-v2' : 'odata';
|
|
334
346
|
}
|
|
335
347
|
let package_json_updated = false;
|
|
336
348
|
for (let service of services) {
|
|
337
349
|
if (!requires[service]) {
|
|
338
350
|
const initial_model = path.relative(cwd, dest);
|
|
339
351
|
const model = isLinux() ? initial_model.replace(/\\/g, '/') : initial_model;
|
|
340
|
-
cds.env.requires[service] = requires[service] = { kind:
|
|
352
|
+
cds.env.requires[service] = requires[service] = { kind: kind, model, ...config };
|
|
341
353
|
await write(package_json, JSON.stringify(conf, null, ' '));
|
|
342
354
|
package_json_updated = true;
|
|
343
355
|
}
|
|
@@ -349,11 +361,12 @@ async function _updatePackageJson(dest, services, version, cwd) {
|
|
|
349
361
|
|
|
350
362
|
async function postProcess(filePath, options, csn, cwd) {
|
|
351
363
|
const services = _getServiceNames(csn);
|
|
364
|
+
let config = await isValidJsonObject(options);
|
|
352
365
|
if (options.out) options.out = _validateAndComputeOutputFilePath(options.out, options.as, filePath);
|
|
353
|
-
const
|
|
354
|
-
const result = await _getResult(csn, options.as, options.out || filePath.replace(/\.[^.]+$/, ''), options.force,
|
|
366
|
+
const kind = options.inputFileKind;
|
|
367
|
+
const result = await _getResult(csn, options.as, options.out || filePath.replace(/\.[^.]+$/, ''), options.force, kind);
|
|
355
368
|
const written = options.dry || await _write(result, services, cwd);
|
|
356
|
-
const registered = options.dry || options.no_save || await _updatePackageJson(result[1].replace(/\.[^.]+$/, ''), services,
|
|
369
|
+
const registered = options.dry || options.no_save || await _updatePackageJson(result[1].replace(/\.[^.]+$/, ''), services, kind, cwd, config);
|
|
357
370
|
if (options.dry) return console.log(result[0]);
|
|
358
371
|
return Promise.all([written, registered]);
|
|
359
372
|
}
|
package/lib/import/importapi.js
CHANGED
|
@@ -91,7 +91,7 @@ async function cds_import_from_openapi(filepath, options = {}) {
|
|
|
91
91
|
filepath = await common.isValidInputFile(path.resolve(process.cwd(), filepath));
|
|
92
92
|
const src = await common.getContentFromFile(filepath);
|
|
93
93
|
let csn = openapi.openAPI2csn(src);
|
|
94
|
-
options.
|
|
94
|
+
options.inputFileKind = 'rest';
|
|
95
95
|
return csn;
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -106,6 +106,6 @@ async function cds_import_from_asyncapi(filepath, options = {}) {
|
|
|
106
106
|
filepath = await common.isValidInputFile(path.resolve(process.cwd(), filepath));
|
|
107
107
|
const src = await common.getContentFromFile(filepath);
|
|
108
108
|
let csn = asyncapi.asyncapi2csn(src);
|
|
109
|
-
options.
|
|
109
|
+
options.inputFileKind = 'odata';
|
|
110
110
|
return csn;
|
|
111
111
|
}
|
package/lib/import/message.js
CHANGED
|
@@ -34,7 +34,9 @@ const messages = {
|
|
|
34
34
|
BINARY_MAXLENGTH: 'WARNING: MaxLength for type Edm.Binary should not exceed 5000.',
|
|
35
35
|
SCALE_GT_PRECISION: 'WARNING: Scale value should not be greater than Precision value.',
|
|
36
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.'
|
|
37
|
+
NULLABLE_KEY: 'WARNING: Key element cannot be Nullable.',
|
|
38
|
+
COLLISION_DETECTED: `INFO: Element name collision detected. Adding suffix to affected type category.`,
|
|
39
|
+
SPECIFY_CONFIG: 'For --config option, please specify correct JSON Object.',
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
function getMessages() {
|
|
@@ -9,7 +9,7 @@ let common = require("../common");
|
|
|
9
9
|
let versionInfo = require("../../../package.json").version;
|
|
10
10
|
let Multimap = require("./util/MultiMap");
|
|
11
11
|
const ignoreObjects = ["$Reference"]; // add all the objects that needs to be ignored for parsing.
|
|
12
|
-
const { warn } = require("../../util/term");
|
|
12
|
+
const { warn, info } = require("../../util/term");
|
|
13
13
|
const MetadataConverterFactory = require("./metadataconverter/MetadataConverterFactory");
|
|
14
14
|
|
|
15
15
|
const edmPrimitiveTypes = [
|
|
@@ -1216,6 +1216,12 @@ function _parseAnnotations(schemaAnnotations, csnDefs, parserContext) {
|
|
|
1216
1216
|
});
|
|
1217
1217
|
}
|
|
1218
1218
|
|
|
1219
|
+
function _addSuffixForCollisionTypes(fqName, suffix) {
|
|
1220
|
+
console.log(info(`INFO: Due to collision in ${fqName} suffix "${suffix}" is added.`));
|
|
1221
|
+
const suffixedFqElementName = (fqName + suffix);
|
|
1222
|
+
return suffixedFqElementName;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1219
1225
|
function _parseEntityContainer(csnDefs, entityContainerElements, parserContext) {
|
|
1220
1226
|
const entityContainerElementNames = Object.keys(entityContainerElements);
|
|
1221
1227
|
const redundantElementNames = [];
|
|
@@ -1241,16 +1247,26 @@ function _parseEntityContainer(csnDefs, entityContainerElements, parserContext)
|
|
|
1241
1247
|
let fqDefRefName = _resolveAliasedElementName(definitionRefName, parserContext);
|
|
1242
1248
|
let fqElementName = parserContext.primarySchema + "." + ecElement;
|
|
1243
1249
|
|
|
1244
|
-
/**
|
|
1245
|
-
* For now we don't support the collision between entitycontainer element name
|
|
1246
|
-
* and any of the schema elemetents like entitytype, complextype, etc.
|
|
1247
|
-
* TODO: In future we might have to support it.
|
|
1248
|
-
*/
|
|
1249
1250
|
if (csnDefs[fqElementName] && csnDefs[fqElementName].kind !== kind) {
|
|
1250
|
-
|
|
1251
|
+
console.log(info(messages.COLLISION_DETECTED));
|
|
1252
|
+
let suffixedFqElementName;
|
|
1253
|
+
// Check the type category that experienced the collision
|
|
1254
|
+
if (parserContext.complexTypes.includes(fqElementName)) {
|
|
1255
|
+
suffixedFqElementName = _addSuffixForCollisionTypes(fqElementName, "_ComplexType");
|
|
1256
|
+
} else if (parserContext.enumTypes.includes(fqElementName)) {
|
|
1257
|
+
suffixedFqElementName = _addSuffixForCollisionTypes(fqElementName, "_EnumType");
|
|
1258
|
+
} else if (parserContext.typeDefinitions.includes(fqElementName)) {
|
|
1259
|
+
suffixedFqElementName = _addSuffixForCollisionTypes(fqElementName, "_TypeDefinition");
|
|
1260
|
+
}
|
|
1261
|
+
// copy the definition with new suffixed name and delete the old definition from csnDefs
|
|
1262
|
+
csnDefs[suffixedFqElementName] = csnDefs[fqElementName];
|
|
1263
|
+
delete csnDefs[fqElementName];
|
|
1251
1264
|
}
|
|
1252
1265
|
|
|
1253
1266
|
if (fqElementName !== fqDefRefName) {
|
|
1267
|
+
if (csnDefs[fqElementName] && !parserContext.entityToEntitySetMap.get(fqElementName)) {
|
|
1268
|
+
fqElementName = _addSuffixForCollisionTypes(fqElementName, "_EntityType");
|
|
1269
|
+
}
|
|
1254
1270
|
csnDefs[fqElementName] = Object.assign({}, csnDefs[fqDefRefName]);
|
|
1255
1271
|
/**
|
|
1256
1272
|
* Capture all the defintion reference element name which are no
|
package/lib/index.js
CHANGED
|
@@ -18,3 +18,8 @@ cds.extend (cds.compile.to.constructor) .with (class {
|
|
|
18
18
|
if (cds.compile.to.constructor.name === 'Object') { // not a class as in cds 7
|
|
19
19
|
cds.compile.to.xsuaa = require('./compile/to-xsuaa')
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
cds.schema = cds.schema || {} // backward compatibility
|
|
23
|
+
cds.extend (cds.schema) .with ( {
|
|
24
|
+
overlay4: require('./env/schema')
|
|
25
|
+
})
|
package/lib/init/constants.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const MAVEN_ARCHETYPE_VERSION = '2.1.0';
|
|
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.3.0',
|
|
12
8
|
|
|
13
9
|
OPTIONS: Object.freeze({
|
|
14
10
|
JAVA: 'java',
|
|
@@ -28,8 +24,9 @@ const constants = {
|
|
|
28
24
|
EXTENSIBILITY: 'extensibility',
|
|
29
25
|
XSUAA: 'xsuaa',
|
|
30
26
|
HANA: 'hana',
|
|
27
|
+
POSTGRES: 'postgres',
|
|
31
28
|
SQLITE: 'sqlite',
|
|
32
|
-
|
|
29
|
+
H2: 'h2',
|
|
33
30
|
ENTERPRISE_MESSAGING: 'enterprise-messaging',
|
|
34
31
|
ENTERPRISE_MESSAGING_SHARED: 'enterprise-messaging-shared',
|
|
35
32
|
FILE_BASED_MESSAGING: 'file-based-messaging',
|
|
@@ -42,12 +39,15 @@ const constants = {
|
|
|
42
39
|
LINT_GLOBAL: 'lint:global',
|
|
43
40
|
LINT_DEV: 'lint:dev',
|
|
44
41
|
HELM: 'helm',
|
|
45
|
-
TYPER: 'typer'
|
|
42
|
+
TYPER: 'typer',
|
|
43
|
+
HTML5_REPO: 'html5-repo'
|
|
46
44
|
}),
|
|
47
45
|
|
|
48
46
|
PROJECT_TYPE: Object.freeze({ nodejs: 1, java: 2, unknown: 3 }),
|
|
49
47
|
|
|
50
48
|
REGEX_JAVA_PACKAGE: /^[a-z_][a-z0-9_]*(\.[a-z_][a-z0-9_]*)*$/g,
|
|
49
|
+
// copied from mbt error message
|
|
50
|
+
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
51
|
REGEX_PROJECT_NAME: /[^0-9a-zA-Z_-]/g,
|
|
52
52
|
|
|
53
53
|
URLS: Object.freeze({
|
|
@@ -57,11 +57,8 @@ const constants = {
|
|
|
57
57
|
MAVEN_ARCHETYPE_HELP: `https://cap.cloud.sap/docs/java/development/#the-maven-archetype`,
|
|
58
58
|
MAVEN_INSTALL_HELP: `https://maven.apache.org/install.html`,
|
|
59
59
|
VSCODE: 'https://code.visualstudio.com/',
|
|
60
|
-
VSCODE_NBVIEWER: 'https://marketplace.visualstudio.com/items?itemName=ms-python.python',
|
|
61
60
|
VSCODE_ESLINT: 'https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint'
|
|
62
61
|
}),
|
|
63
62
|
|
|
64
63
|
PROJECT_FILES: ['package.json', 'pom.xml', '.cdsrc.json']
|
|
65
64
|
}
|
|
66
|
-
|
|
67
|
-
module.exports = constants;
|