@sap/cds-dk 4.5.4 → 4.6.4
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 +61 -0
- package/bin/bind.js +124 -8
- package/bin/cds-ts.js +1 -1
- package/bin/cds.js +1 -0
- package/bin/compile/index.js +2 -1
- package/bin/env.js +50 -9
- package/bin/help.js +1 -0
- package/bin/import.js +34 -14
- package/bin/repl.js +6 -2
- package/bin/watch.js +40 -10
- package/lib/bind/bindingResolver.js +138 -0
- package/lib/bind/cfKeyProvider.js +353 -0
- package/lib/bind/userCdsrcJsonStorageProvider.js +144 -0
- package/lib/client/activate.js +3 -9
- package/lib/client/auth_manager.js +4 -4
- package/lib/client/extend.js +4 -9
- package/lib/client/settings_manager.js +6 -5
- package/lib/import/common.js +10 -1
- package/lib/import/importapi.js +4 -3
- package/lib/import/index.js +37 -7
- package/lib/import/message.js +1 -0
- package/lib/import/utils/MetadataConverter.js +25 -4
- package/lib/import/utils/MetadataConverterFactory.js +1 -2
- package/lib/import/utils/MultiMap.js +26 -0
- package/lib/import/v2parser.js +138 -16
- package/lib/import/v4parser.js +212 -172
- package/lib/init/constants.js +1 -1
- package/lib/init/template/data/index.js +11 -1
- package/lib/init/template/hana/index.js +16 -20
- package/lib/init/template/lint/files/.eslint/rules/no-entity-moo.js +36 -52
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/rule.test.js +15 -0
- package/lib/init/template/lint/index.js +19 -17
- package/lib/init/template/mta/files/mta.yaml.hbs +0 -7
- package/lib/init/template/mta/projectReader.js +1 -3
- package/lib/init/template/mtx/files/xs-security.json.hbs +3 -1
- package/lib/init/template/mtx/package/dependencies.package.json +1 -1
- package/lib/init/template/nodejs/files/.gitignore.hbs +1 -0
- package/lib/init/template/samples/index.js +1 -1
- package/lib/init/template/templateBase.js +10 -41
- package/lib/init/util/commandUtil.js +5 -0
- package/lib/lint/Linter.js +19 -28
- package/lib/lint/generators.js +5 -4
- package/lib/lint/io.js +76 -51
- package/lib/util/term.js +2 -0
- package/lib/watch/node-watch.js +38 -19
- package/npm-shrinkwrap.json +1053 -1279
- package/package.json +3 -3
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/test.js +0 -10
|
@@ -26,12 +26,12 @@ function getTokenUrl(params) {
|
|
|
26
26
|
tokenUrl.search = `subdomain=${params.get('subdomain')}`;
|
|
27
27
|
|
|
28
28
|
if (params.has('refreshToken')) {
|
|
29
|
-
tokenUrl.search += `&refresh_token=${params.get('refreshToken')}`;
|
|
29
|
+
tokenUrl.search += `&refresh_token=${encodeURIComponent(params.get('refreshToken'))}`;
|
|
30
30
|
} else if (params.has('passcode')) {
|
|
31
|
-
tokenUrl.search += `&passcode=${params.get('passcode')}`;
|
|
31
|
+
tokenUrl.search += `&passcode=${encodeURIComponent(params.get('passcode'))}`;
|
|
32
32
|
}
|
|
33
33
|
if (params.has('clientid')) {
|
|
34
|
-
tokenUrl.search += `&clientid=${params.get('clientid')}&clientsecret=${params.get('clientsecret')}`;
|
|
34
|
+
tokenUrl.search += `&clientid=${encodeURIComponent(params.get('clientid'))}&clientsecret=${encodeURIComponent(params.get('clientsecret'))}`;
|
|
35
35
|
}
|
|
36
36
|
return tokenUrl.toString();
|
|
37
37
|
}
|
|
@@ -58,7 +58,7 @@ async function retrieveTokenOrPasscodeUrl(params) {
|
|
|
58
58
|
|
|
59
59
|
let response;
|
|
60
60
|
try {
|
|
61
|
-
response = await axios(tokenUrl
|
|
61
|
+
response = await axios.get(tokenUrl);
|
|
62
62
|
} catch (error) {
|
|
63
63
|
throw new Error(`Failed to get authentication token: ${error}.\nResponse body: ${util.inspect(response && response.data)}`);
|
|
64
64
|
}
|
package/lib/client/extend.js
CHANGED
|
@@ -104,15 +104,12 @@ module.exports = class Extend extends BaseCommand {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
static async runExtend(params) {
|
|
107
|
-
const
|
|
108
|
-
const requestOptions = Object.assign({
|
|
109
|
-
method: 'GET',
|
|
110
|
-
retry: 0
|
|
111
|
-
}, params.get('reqAuth'));
|
|
107
|
+
const contentUrl = Extend.getMtxApiUrl(params.get('appUrl'), COMMAND_URL_SUFFIX);
|
|
108
|
+
const requestOptions = Object.assign({ retry: 0 }, params.get('reqAuth'));
|
|
112
109
|
|
|
113
110
|
Logger.log('Retrieving files...');
|
|
114
111
|
|
|
115
|
-
const [ response ] = (await axios(
|
|
112
|
+
const [ response ] = (await axios.get(contentUrl, requestOptions)).data;
|
|
116
113
|
|
|
117
114
|
Logger.log('\nWriting files to: ' + params.get('projectFolder'));
|
|
118
115
|
|
|
@@ -142,11 +139,9 @@ module.exports = class Extend extends BaseCommand {
|
|
|
142
139
|
}
|
|
143
140
|
|
|
144
141
|
if (params.get('wsdl')) {
|
|
145
|
-
|
|
146
142
|
Logger.log('Writing custom files');
|
|
147
|
-
|
|
148
143
|
const customFileUrl = Extend.getMtxApiUrl(params.get('appUrl'), Extend.CUSTOM_FILE_URL_SUFFIX);
|
|
149
|
-
const customFiles = (await axios(customFileUrl,
|
|
144
|
+
const customFiles = (await axios.get(customFileUrl, requestOptions)).data;
|
|
150
145
|
await Extend.storeCustomFiles(params.get('projectFolder'), customFiles);
|
|
151
146
|
}
|
|
152
147
|
}
|
|
@@ -55,7 +55,7 @@ function other(tokenStorage) {
|
|
|
55
55
|
|
|
56
56
|
function getProjectFolder(params) {
|
|
57
57
|
const relativePath = params.get('projectFolder') || params.get('directory') || (
|
|
58
|
-
params.has('subdomain') && fse.existsSync(
|
|
58
|
+
params.has('subdomain') && fse.existsSync(params.get('subdomain'))
|
|
59
59
|
? params.get('subdomain')
|
|
60
60
|
: '.'
|
|
61
61
|
);
|
|
@@ -138,7 +138,7 @@ class SettingsManager {
|
|
|
138
138
|
if (params.get('clearOtherTokenStorage')) {
|
|
139
139
|
// Delete token from the other storage.
|
|
140
140
|
params.delete('clearOtherTokenStorage');
|
|
141
|
-
await this.deleteToken(params,
|
|
141
|
+
await this.deleteToken(params, true);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
if (params.get('tokenStorage') === 'plain') {
|
|
@@ -303,15 +303,16 @@ class SettingsManager {
|
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
static async deleteToken(params,
|
|
306
|
+
static async deleteToken(params, fromOtherStorage = false) {
|
|
307
307
|
const allParams = params.clone();
|
|
308
308
|
await this.loadAndMergeSettings(allParams, true);
|
|
309
309
|
|
|
310
310
|
const target = `URL ${allParams.get('appUrl')}, subdomain '${allParams.get('subdomain')}'`;
|
|
311
|
-
|
|
312
|
-
|
|
311
|
+
let fromStorage = fromOtherStorage && other(params.get('tokenStorage'))
|
|
313
312
|
const deleteBoth = !fromStorage && allParams.get('tokenStorage') && allParams.get('clearOtherTokenStorage');
|
|
314
313
|
|
|
314
|
+
Logger.debug(`Deleting authentication data${fromOtherStorage ? ' from other storage' : ''} for ${target}`);
|
|
315
|
+
|
|
315
316
|
fromStorage = fromStorage || allParams.get('tokenStorage');
|
|
316
317
|
if (!fromStorage) {
|
|
317
318
|
Logger.log(`No authentication data found for ${target}`);
|
package/lib/import/common.js
CHANGED
|
@@ -30,6 +30,14 @@ function checkForEmptyKeys(element, oDataVersion) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
//to replace the alias value with original schema namespace value
|
|
34
|
+
function replaceAliasValue(schemaContent, schemaNamespaceValue, aliasValue){
|
|
35
|
+
const re = RegExp(`([("/])${aliasValue}([/").])`, 'g');
|
|
36
|
+
const val = `$1${schemaNamespaceValue}$2`;
|
|
37
|
+
schemaContent = schemaContent.replace(re, val);
|
|
38
|
+
return JSON.parse(schemaContent);
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
async function _getODataVersion(edmx) {
|
|
34
42
|
let oDataVersion;
|
|
35
43
|
let jsonObj;
|
|
@@ -115,5 +123,6 @@ module.exports = {
|
|
|
115
123
|
getVersion,
|
|
116
124
|
getSuffix,
|
|
117
125
|
supportedFormats,
|
|
118
|
-
checkForEmptyKeys
|
|
126
|
+
checkForEmptyKeys,
|
|
127
|
+
replaceAliasValue
|
|
119
128
|
};
|
package/lib/import/importapi.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const edm2csn = require('.');
|
|
2
|
-
|
|
2
|
+
let context={};
|
|
3
|
+
context.oDataInfo = {};
|
|
4
|
+
context.namespaces = [];
|
|
3
5
|
/**
|
|
4
6
|
* This is the import API function to generate CSN from XML/EDMX files.
|
|
5
7
|
* @param {string} filepath
|
|
@@ -10,11 +12,10 @@ const edm2csn = require('.');
|
|
|
10
12
|
* - other import options not supported
|
|
11
13
|
* @returns {object} CSN
|
|
12
14
|
*/
|
|
13
|
-
const oDataInfo={};
|
|
14
15
|
module.exports = async function cds_import(filepath, options = {keepNamespace : false}) //namespace will be taken as false in case it is not mentioned
|
|
15
16
|
{
|
|
16
17
|
filepath = await edm2csn.isValidInputFile(filepath);
|
|
17
18
|
let edmx= await edm2csn.getMetadataFromFile(filepath);
|
|
18
|
-
let csn = await edm2csn.edmx2csn(edmx, !options.keepNamespace && filepath,
|
|
19
|
+
let csn = await edm2csn.edmx2csn(edmx, !options.keepNamespace && filepath, context);
|
|
19
20
|
return csn;
|
|
20
21
|
}
|
package/lib/import/index.js
CHANGED
|
@@ -116,23 +116,32 @@ function _convertEDMXWithMultipleSchemas(edmx, multipleSchemas, namespace) {
|
|
|
116
116
|
</Schema>
|
|
117
117
|
</edmx:DataServices>
|
|
118
118
|
*/
|
|
119
|
-
|
|
119
|
+
|
|
120
|
+
// Remove the ending and beginning of the first and second schema tag
|
|
121
|
+
edmx = edmx.replace(multipleSchemas, '');
|
|
122
|
+
// Now capture the schema tag details
|
|
123
|
+
let schemaDetails = edmx.match(/(<Schema [^>]*>)/);
|
|
124
|
+
// Replace the Namespace value with the correct one
|
|
125
|
+
schemaDetails = schemaDetails[0].replace(/Namespace="[^"]+"/, `Namespace="${namespace}"`);
|
|
126
|
+
// Place the above computed schema details in the edmx content
|
|
127
|
+
edmx = edmx.replace(/(<Schema [^>]*>)/, `${schemaDetails}`);
|
|
120
128
|
|
|
121
129
|
return edmx;
|
|
122
130
|
}
|
|
123
131
|
|
|
124
132
|
async function edmx2csn(edmx, filename, context) {
|
|
125
|
-
const result = edmx.match(/EntityType="([
|
|
133
|
+
const result = edmx.match(/EntityType="([^"]+)/);
|
|
126
134
|
const multipleSchemas = /(\s*<\/Schema>\s*<Schema [^>]*>)\s*/m;
|
|
127
135
|
if (!result) {
|
|
128
136
|
throw new Error(messages.MISSING_ENTITIES);
|
|
129
137
|
}
|
|
130
|
-
const
|
|
138
|
+
const index = result[1].lastIndexOf(".");
|
|
139
|
+
const namespace = (index > 0 ) ? result[1].substring(0, index) : result[1];
|
|
131
140
|
|
|
132
141
|
if (multipleSchemas.test(edmx)) {
|
|
133
142
|
edmx = _convertEDMXWithMultipleSchemas(edmx, multipleSchemas, namespace);
|
|
134
143
|
}
|
|
135
|
-
let csn = await generateCSN(edmx, true, context);
|
|
144
|
+
let csn = await generateCSN(edmx, false, true, context);
|
|
136
145
|
if (filename) {
|
|
137
146
|
// Replace namespace by given filename in the csn file instead of edmx file
|
|
138
147
|
const re = RegExp(`([("/])${namespace}([/").])`, 'g');
|
|
@@ -151,20 +160,33 @@ async function _generateEDMX2JSON(edmx) {
|
|
|
151
160
|
return JSON.parse(cjson);
|
|
152
161
|
}
|
|
153
162
|
|
|
154
|
-
async function generateCSN(edmx, mockServerUc, context) {
|
|
163
|
+
async function generateCSN(edmx, ignorePersistenceSkip, mockServerUc, context) {
|
|
155
164
|
let csn;
|
|
156
165
|
let odataVersion;
|
|
157
166
|
let edmx2jsonModel;
|
|
167
|
+
let defaultNamespaces;
|
|
168
|
+
let namespaces = [];
|
|
169
|
+
if(context.namespaces) namespaces.push(...(context.namespaces));
|
|
158
170
|
odataVersion = await common.getVersion(edmx);
|
|
159
171
|
if (odataVersion === "V2") {
|
|
160
172
|
if(context){
|
|
161
173
|
context.oDataVersion = "V2";
|
|
162
174
|
}
|
|
175
|
+
//default namespaces to be present
|
|
176
|
+
if(!context.include_all_namespaces)
|
|
177
|
+
{
|
|
178
|
+
defaultNamespaces = ['edmx','sap','m'];
|
|
179
|
+
namespaces = namespaces.concat(defaultNamespaces);
|
|
180
|
+
namespaces = namespaces.filter((item,pos) => namespaces.indexOf(item) === pos); //removing duplicate values
|
|
181
|
+
}
|
|
182
|
+
context.namespaces = namespaces;
|
|
163
183
|
edmx2jsonModel = await _generateEDMX2JSON(edmx);
|
|
164
184
|
return new Promise(function getCsn(resolve, reject) {
|
|
165
185
|
csn = edmxv2CSN.getEdmxv2CSN(
|
|
166
186
|
edmx2jsonModel,
|
|
167
|
-
|
|
187
|
+
ignorePersistenceSkip,
|
|
188
|
+
mockServerUc,
|
|
189
|
+
context
|
|
168
190
|
);
|
|
169
191
|
if (csn) {
|
|
170
192
|
resolve(csn);
|
|
@@ -177,7 +199,15 @@ async function generateCSN(edmx, mockServerUc, context) {
|
|
|
177
199
|
if(context){
|
|
178
200
|
context.oDataVersion = "V4";
|
|
179
201
|
}
|
|
180
|
-
|
|
202
|
+
//default namespaces to be present
|
|
203
|
+
if(!context.include_all_namespaces)
|
|
204
|
+
{
|
|
205
|
+
defaultNamespaces = ['edmx'];
|
|
206
|
+
namespaces = namespaces.concat(defaultNamespaces);
|
|
207
|
+
namespaces = namespaces.filter((item,pos) => namespaces.indexOf(item) === pos); //removing duplicate values
|
|
208
|
+
}
|
|
209
|
+
context.namespaces = namespaces;
|
|
210
|
+
return edmxv4CSN.getEdmxv4CSN(edmx, ignorePersistenceSkip, mockServerUc, context);
|
|
181
211
|
}
|
|
182
212
|
return new Promise(function invalidFile(resolve, reject) {
|
|
183
213
|
reject(new Error(messages.INVALID_EDMX_METADATA));
|
package/lib/import/message.js
CHANGED
|
@@ -13,6 +13,7 @@ const messages = {
|
|
|
13
13
|
OUTPUT_FILE_MISMATCH: 'Specify the file extension that --as option supports.',
|
|
14
14
|
INVALID_AS_OPTION: 'For --as option, please specify one of these output file format [cds, csn, json].',
|
|
15
15
|
INCORRECT_EXTENSION: 'If --as option is not provided, then please provide either .json or .csn output file extension.',
|
|
16
|
+
INCORRECT_INPUT_NAMESPACES: 'Invalid value for --include-namespaces option. Specify namespaces as comma-separated list or "*"'
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
function getMessages() {
|
|
@@ -396,7 +396,7 @@ class Expression {
|
|
|
396
396
|
const target = this.getTarget();
|
|
397
397
|
const element = this.getElement();
|
|
398
398
|
const attribs = element.attributes;
|
|
399
|
-
|
|
399
|
+
let custom_namespace = MetadataConverter.custom_namespace;
|
|
400
400
|
if (attribs == null) return;
|
|
401
401
|
let realType = attribs.Type || attribs.EntityType || attribs.UnderlyingType;
|
|
402
402
|
const propertyName = attribs.UnderlyingType == null ? '$Type' : '$UnderlyingType';
|
|
@@ -574,9 +574,29 @@ class Expression {
|
|
|
574
574
|
!(target.$Kind === 'NavigationProperty' && target.$Collection === true)) {
|
|
575
575
|
target.$Nullable = true;
|
|
576
576
|
}
|
|
577
|
+
const nonStdAttributes = this.getNonStdAttributes(element.attributes);
|
|
578
|
+
for (let i = 0; i < nonStdAttributes.length; i++) {
|
|
579
|
+
let attr = nonStdAttributes[i];
|
|
580
|
+
let [prefix, attrName] = attr.split(':');
|
|
581
|
+
if(attrName)
|
|
582
|
+
attrName = attrName.replace(/-/g,'.');
|
|
583
|
+
let attribute;
|
|
584
|
+
if ((custom_namespace.includes(prefix) && attrName !== undefined) || custom_namespace.includes(true))
|
|
585
|
+
attribute = "@" + prefix + "." + attrName;
|
|
586
|
+
else if (attrName === undefined)
|
|
587
|
+
attribute = "$" + prefix;
|
|
588
|
+
if (attribute !== undefined)
|
|
589
|
+
target[attribute] = element.attributes[attr];
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
getNonStdAttributes(attributes) {
|
|
593
|
+
const attributeList = [];
|
|
594
|
+
const standardArrtibutes = ['MaxLength', 'Precision', 'Scale', 'Nullable', 'DefaultValue',
|
|
595
|
+
'Name', 'Type', 'HasStream', 'IsFlags', 'Alias', 'Namespace', 'BaseType', 'Partner',
|
|
596
|
+
'IsComposable', 'ContainsTarget', 'IsBound', 'EntitySet', 'EntitySetPath', 'EntityType'];
|
|
597
|
+
attributeList.push(...Object.keys(attributes).filter(attribute => standardArrtibutes.indexOf(attribute) === -1));
|
|
598
|
+
return attributeList;
|
|
577
599
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
600
|
}
|
|
581
601
|
|
|
582
602
|
class EdmPrimitiveType {
|
|
@@ -2361,7 +2381,8 @@ class OnDeleteExpression extends Expression {
|
|
|
2361
2381
|
}
|
|
2362
2382
|
|
|
2363
2383
|
|
|
2364
|
-
MetadataConverter.createOdataV4MetadataXmlToOdataV4CsdlStrategy = () => {
|
|
2384
|
+
MetadataConverter.createOdataV4MetadataXmlToOdataV4CsdlStrategy = (name_space) => {
|
|
2385
|
+
MetadataConverter.custom_namespace = name_space;
|
|
2365
2386
|
return new MetadataConverter.DefaultXmlStrategy()
|
|
2366
2387
|
.use('http://docs.oasis-open.org/odata/ns/edmx:Edmx', (element, parentExpression, context) => {
|
|
2367
2388
|
return new EdmxRootExpression(element, null, context);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
3
2
|
const xmljs = require('xml-js');
|
|
4
3
|
const MetadataConverterV4 = require('../utils/MetadataConverter');
|
|
5
4
|
|
|
@@ -61,7 +60,7 @@ class MetadataConverterFactory {
|
|
|
61
60
|
*
|
|
62
61
|
*/
|
|
63
62
|
static createEdmxV40XmlToV40Json(options = {}) {
|
|
64
|
-
const defaultStrategy = MetadataConverterV4.createOdataV4MetadataXmlToOdataV4CsdlStrategy();
|
|
63
|
+
const defaultStrategy = MetadataConverterV4.createOdataV4MetadataXmlToOdataV4CsdlStrategy(options);
|
|
65
64
|
|
|
66
65
|
if (options.astFactory) {
|
|
67
66
|
defaultStrategy.setASTFactory(options.astFactory);
|
|
@@ -67,6 +67,32 @@ var Multimap = (function () {
|
|
|
67
67
|
return this;
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* @return {Array} all the keys in the map
|
|
72
|
+
*/
|
|
73
|
+
Multimap.prototype.keys = function () {
|
|
74
|
+
if (this._map)
|
|
75
|
+
return makeIterator(this._.keys());
|
|
76
|
+
|
|
77
|
+
return makeIterator(Object.keys(this._));
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
function makeIterator(iterator) {
|
|
81
|
+
if (Array.isArray(iterator)) {
|
|
82
|
+
var nextIndex = 0;
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
next: function () {
|
|
86
|
+
return nextIndex < iterator.length ?
|
|
87
|
+
{ value: iterator[nextIndex++], done: false } :
|
|
88
|
+
{ done: true };
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return iterator;
|
|
94
|
+
}
|
|
95
|
+
|
|
70
96
|
return Multimap;
|
|
71
97
|
})();
|
|
72
98
|
|