@sap/cds-dk 4.5.0 → 4.6.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 +65 -9
- package/bin/bind.js +124 -8
- package/bin/cds-ts.js +1 -1
- package/bin/cds.js +1 -0
- package/bin/deploy/index.js +2 -1
- package/bin/env.js +50 -9
- package/bin/help.js +2 -1
- package/bin/import.js +126 -24
- package/bin/watch.js +45 -9
- package/lib/bind/bindingResolver.js +138 -0
- package/lib/bind/cfKeyProvider.js +353 -0
- package/lib/bind/userCdsrcJsonStorageProvider.js +144 -0
- package/lib/client/auth_manager.js +4 -4
- package/lib/client/settings_manager.js +1 -1
- package/lib/import/common.js +33 -1
- package/lib/import/importapi.js +4 -3
- package/lib/import/index.js +49 -23
- package/lib/import/message.js +11 -6
- 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 +142 -24
- package/lib/import/v4parser.js +222 -149
- package/lib/init/constants.js +1 -1
- package/lib/init/index.js +2 -0
- 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 +14 -6
- package/lib/init/template/mta/files/mta.yaml.hbs +0 -7
- 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 +3 -0
- package/lib/init/util/commandUtil.js +5 -0
- package/lib/lint/Linter.js +31 -20
- package/lib/lint/checks.js +5 -1
- package/lib/lint/generators.js +1 -1
- package/lib/lint/io.js +59 -35
- package/lib/util/term.js +2 -0
- package/lib/watch/node-watch.js +38 -19
- package/npm-shrinkwrap.json +1276 -1102
- package/package.json +4 -3
- package/lib/init/template/lint/files/.eslint/tests/no-entity-moo/test.js +0 -10
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const readline = require('readline');
|
|
4
|
+
|
|
5
|
+
const { fse } = require('@sap/cds-foss');
|
|
6
|
+
const { highlight, warn } = require('../util/term');
|
|
7
|
+
|
|
8
|
+
const commandUtil = require('../init/util/commandUtil');
|
|
9
|
+
|
|
10
|
+
const CDSRC_FILENAME = '.cdsrc-private.json';
|
|
11
|
+
const GIT_IGNORE_FILE = '.gitignore';
|
|
12
|
+
|
|
13
|
+
class UserCdsrcJsonStorageProvider {
|
|
14
|
+
|
|
15
|
+
async askBooleanQuestion(question) {
|
|
16
|
+
const rl = readline.createInterface({
|
|
17
|
+
input: process.stdin,
|
|
18
|
+
output: process.stdout
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
question = question.trim() + ' ';
|
|
22
|
+
return new Promise(resolve => {
|
|
23
|
+
rl.question(question, answer => {
|
|
24
|
+
if (answer === 'y' || answer === 'Y') {
|
|
25
|
+
resolve(true);
|
|
26
|
+
} else {
|
|
27
|
+
resolve(false);
|
|
28
|
+
}
|
|
29
|
+
rl.close();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async ensureFileIsGitignored(file, logger = console) {
|
|
35
|
+
try {
|
|
36
|
+
await commandUtil.spawnCommand('git', ['check-ignore', '-n', '-v', file], {}, undefined, true);
|
|
37
|
+
return; // file is git ignored
|
|
38
|
+
} catch (err) {
|
|
39
|
+
if (err.code === 'ENOENT') { // git command not found
|
|
40
|
+
logger.debug('git command not found');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!err.stdLog.match(/::/)) { // included or not a git repo
|
|
45
|
+
logger.debug(err);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
logger.log(`adding entry '${file}' to ${GIT_IGNORE_FILE}.`);
|
|
51
|
+
let gitignore = await this.readFileSafely(GIT_IGNORE_FILE);
|
|
52
|
+
gitignore = gitignore + `
|
|
53
|
+
# added by cds bind
|
|
54
|
+
${file}
|
|
55
|
+
`;
|
|
56
|
+
await fse.writeFile(GIT_IGNORE_FILE, gitignore, 'utf8');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async readFileSafely(file) {
|
|
60
|
+
try {
|
|
61
|
+
return await fse.readFile(file, 'utf8');
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async storeServices(services, options = {}) {
|
|
68
|
+
let jsonPath = options['output-file'] || CDSRC_FILENAME;
|
|
69
|
+
|
|
70
|
+
if (jsonPath === CDSRC_FILENAME) {
|
|
71
|
+
await this.ensureFileIsGitignored(CDSRC_FILENAME, options.logger);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
jsonPath = jsonPath.replace(/~/g, os.homedir());
|
|
75
|
+
// const stat = await fse.stat(jsonPath);
|
|
76
|
+
if (path.extname(jsonPath) !== '.json') {
|
|
77
|
+
jsonPath = path.join(jsonPath, '.cdsrc.json');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// if (options['save-credentials'] && services.find(service => service.credentials) && process.stdin.isTTY) {
|
|
81
|
+
// const answer = await this.askBooleanQuestion(warn(`IF YOU CONTINUE, SENSITIVE DATA WILL BE WRITTEN TO AN UNENCRYPTED FILE [${jsonPath}]! Do you want to continue [y/N] ?`));
|
|
82
|
+
// if (!answer) {
|
|
83
|
+
// options.logger.log(`aborting cds bind; not writing any sensitive data to file [${jsonPath}]`);
|
|
84
|
+
// return;
|
|
85
|
+
// }
|
|
86
|
+
// }
|
|
87
|
+
|
|
88
|
+
const jsonObject = await this.readJsonSafely(jsonPath);
|
|
89
|
+
let cdsRoot;
|
|
90
|
+
if (path.basename(jsonPath) === 'package.json') {
|
|
91
|
+
jsonObject.cds = jsonObject.cds || {};
|
|
92
|
+
cdsRoot = jsonObject.cds;
|
|
93
|
+
} else {
|
|
94
|
+
cdsRoot = jsonObject;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const profileKey = `[${options.profile}]`;
|
|
98
|
+
cdsRoot.features = cdsRoot.features || {};
|
|
99
|
+
cdsRoot.features[profileKey] = cdsRoot.features[profileKey] || {};
|
|
100
|
+
cdsRoot.features[profileKey].emulate_vcap_services = true;
|
|
101
|
+
|
|
102
|
+
cdsRoot.requires = cdsRoot.requires || {};
|
|
103
|
+
let section;
|
|
104
|
+
if (options.profile) {
|
|
105
|
+
section = cdsRoot.requires[profileKey] = cdsRoot.requires[profileKey] || {};
|
|
106
|
+
} else {
|
|
107
|
+
section = cdsRoot.requires;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
for (const service of services) {
|
|
111
|
+
const { name, kind, binding, credentials } = service;
|
|
112
|
+
const requireService = section[name] = section[name] || {};
|
|
113
|
+
|
|
114
|
+
Object.assign(requireService, { kind, binding, credentials });
|
|
115
|
+
|
|
116
|
+
options.logger.log(`Binding ${highlight(name)} to Cloud Foundry service ${highlight(binding.instance)} key ${highlight(binding.key)} with kind ${highlight(kind)}.`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await this.writeJsonFile(jsonPath, jsonObject, options.logger);
|
|
120
|
+
if (options.profile) {
|
|
121
|
+
options.logger.log(`Saving bindings to ${highlight(jsonPath)} in profile ${highlight(options.profile)} .`);
|
|
122
|
+
} else {
|
|
123
|
+
options.logger.log(`Saving bindings to ${highlight(jsonPath)}.`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async readJsonSafely(filePath) {
|
|
128
|
+
try {
|
|
129
|
+
return await fse.readJSON(filePath) || {};
|
|
130
|
+
} catch (err) {
|
|
131
|
+
return {};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async writeJsonFile(filePath, content) {
|
|
136
|
+
await fse.outputJSON(filePath, content, {
|
|
137
|
+
spaces: 2
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await fse.chmod(filePath, 0o600); // -rw-------
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = new UserCdsrcJsonStorageProvider();
|
|
@@ -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(tokenUrl, { method: 'GET' });
|
|
62
62
|
} catch (error) {
|
|
63
63
|
throw new Error(`Failed to get authentication token: ${error}.\nResponse body: ${util.inspect(response && response.data)}`);
|
|
64
64
|
}
|
|
@@ -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
|
);
|
package/lib/import/common.js
CHANGED
|
@@ -21,6 +21,23 @@ async function _generateEDMX2JSON(edmx) {
|
|
|
21
21
|
return JSON.parse(cjson);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
function checkForEmptyKeys(element, oDataVersion) {
|
|
25
|
+
if (oDataVersion === "V2") {
|
|
26
|
+
return (element.includes(["cds.Association"]) && !element.includes(['"on":']));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
return (['cds.Association'].includes(element.type)) && !element.on && !element.keys;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
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
|
+
|
|
24
41
|
async function _getODataVersion(edmx) {
|
|
25
42
|
let oDataVersion;
|
|
26
43
|
let jsonObj;
|
|
@@ -91,6 +108,21 @@ async function getVersion(fileInputStream) {
|
|
|
91
108
|
return oDataVersion;
|
|
92
109
|
}
|
|
93
110
|
|
|
111
|
+
function getSuffix(outputFormat) {
|
|
112
|
+
const suffix = { edmx: 'xml', "edmx-v2": 'xml', "edmx-v4": 'xml', "edmx-w4": 'xml',
|
|
113
|
+
"edmx-x4": 'xml', openapi: 'openapi3.json', sql: 'sql', edm: 'json', "cdl": "cds" };
|
|
114
|
+
// find the output file extension based on the target conversion selected
|
|
115
|
+
return (outputFormat) ? (suffix[outputFormat] || outputFormat) : 'csn';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function supportedFormats() {
|
|
119
|
+
return ["cds", "csn", "json"];
|
|
120
|
+
}
|
|
121
|
+
|
|
94
122
|
module.exports = {
|
|
95
|
-
getVersion
|
|
123
|
+
getVersion,
|
|
124
|
+
getSuffix,
|
|
125
|
+
supportedFormats,
|
|
126
|
+
checkForEmptyKeys,
|
|
127
|
+
replaceAliasValue
|
|
96
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
|
@@ -42,36 +42,35 @@ async function isValidInputFile(inputFileLoc) {
|
|
|
42
42
|
return file;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function validateAndComputeOutputFilePath(outputFileLoc) {
|
|
46
|
-
let
|
|
45
|
+
function validateAndComputeOutputFilePath(outputFileLoc, as) {
|
|
46
|
+
let index, extension, expectedExtension, supportedExtension;
|
|
47
47
|
if (typeof(outputFileLoc) !== 'boolean') {
|
|
48
48
|
index = outputFileLoc.lastIndexOf(".");
|
|
49
49
|
extension = outputFileLoc.substring(index + 1);
|
|
50
|
+
expectedExtension = (as) ? common.getSuffix(as) : "csn";
|
|
51
|
+
supportedExtension = (index > 0) ? common.supportedFormats().includes(extension) : false;
|
|
50
52
|
}
|
|
53
|
+
|
|
51
54
|
if (outputFileLoc === true || // if output file is not mentioned
|
|
55
|
+
outputFileLoc[0] === "-" || // if output file is missing and "--as" is considered as output file
|
|
52
56
|
outputFileLoc.trim() === "") {
|
|
53
57
|
throw new Error(messages.SPECIFY_OUTPUT_FILE);
|
|
54
|
-
} else if (
|
|
55
|
-
|
|
56
|
-
} else if (index > 0 &&
|
|
57
|
-
!extension.endsWith("/") && // to ignore "../test/lib/import/output/tempOutput/"
|
|
58
|
-
!extension.endsWith("\\") && // to ignore "c:\windows\output\test\"
|
|
59
|
-
extension !== 'json' &&
|
|
60
|
-
extension !== 'csn') {
|
|
61
|
-
// Output file extension is not either .json or .csn ("test.abcde", "./srv/external/test.abcde")
|
|
58
|
+
} else if (index > 0 && !supportedExtension) {
|
|
59
|
+
// extension not supported, eg., "test.abs", "./sv/external.test.abcs", "./a."
|
|
62
60
|
throw new Error(messages.INVALID_OUTPUT_FILE);
|
|
63
|
-
} else if (!
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
//
|
|
68
|
-
|
|
61
|
+
} else if (index > 0 && !as && extension != "json" && extension != "csn") {
|
|
62
|
+
// --as option is not provided but output file is like: "test.cds", "./srv/external/test.cds"
|
|
63
|
+
throw new Error(messages.INCORRECT_EXTENSION);
|
|
64
|
+
} else if (index > 0 && as && extension != expectedExtension){
|
|
65
|
+
// extension not supported by --as option, e.g., "cds import <file> --out test.yml --as cds"
|
|
66
|
+
throw new Error(messages.OUTPUT_FILE_MISMATCH);
|
|
67
|
+
} else if (supportedExtension || (!outputFileLoc.endsWith("\\") && !outputFileLoc.endsWith("/"))) {
|
|
68
|
+
// eg., "./a.csn", "tst.cds", "./srv/external/test", "test", "c:\windows\output\test"
|
|
69
|
+
return outputFileLoc;
|
|
69
70
|
} else if (outputFileLoc.endsWith("/") || outputFileLoc.endsWith("\\")){
|
|
70
71
|
// File name not specified ("./srv/external/", "c:\windows\output\test\")
|
|
71
|
-
|
|
72
|
+
return outputFileLoc + path.parse(defaultFileName).name;
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
-
return filePath;
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
async function getMetadataFromFile(path) {
|
|
@@ -117,7 +116,15 @@ function _convertEDMXWithMultipleSchemas(edmx, multipleSchemas, namespace) {
|
|
|
117
116
|
</Schema>
|
|
118
117
|
</edmx:DataServices>
|
|
119
118
|
*/
|
|
120
|
-
|
|
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}`);
|
|
121
128
|
|
|
122
129
|
return edmx;
|
|
123
130
|
}
|
|
@@ -156,17 +163,29 @@ async function generateCSN(edmx, ignorePersistenceSkip, mockServerUc, context) {
|
|
|
156
163
|
let csn;
|
|
157
164
|
let odataVersion;
|
|
158
165
|
let edmx2jsonModel;
|
|
166
|
+
let defaultNamespaces;
|
|
167
|
+
let namespaces = [];
|
|
168
|
+
if(context.namespaces) namespaces.push(...(context.namespaces));
|
|
159
169
|
odataVersion = await common.getVersion(edmx);
|
|
160
170
|
if (odataVersion === "V2") {
|
|
161
171
|
if(context){
|
|
162
172
|
context.oDataVersion = "V2";
|
|
163
173
|
}
|
|
174
|
+
//default namespaces to be present
|
|
175
|
+
if(!context.include_all_namespaces)
|
|
176
|
+
{
|
|
177
|
+
defaultNamespaces = ['edmx','sap','m'];
|
|
178
|
+
namespaces = namespaces.concat(defaultNamespaces);
|
|
179
|
+
namespaces = namespaces.filter((item,pos) => namespaces.indexOf(item) === pos); //removing duplicate values
|
|
180
|
+
}
|
|
181
|
+
context.namespaces = namespaces;
|
|
164
182
|
edmx2jsonModel = await _generateEDMX2JSON(edmx);
|
|
165
183
|
return new Promise(function getCsn(resolve, reject) {
|
|
166
184
|
csn = edmxv2CSN.getEdmxv2CSN(
|
|
167
185
|
edmx2jsonModel,
|
|
168
186
|
ignorePersistenceSkip,
|
|
169
|
-
mockServerUc
|
|
187
|
+
mockServerUc,
|
|
188
|
+
context
|
|
170
189
|
);
|
|
171
190
|
if (csn) {
|
|
172
191
|
resolve(csn);
|
|
@@ -179,13 +198,20 @@ async function generateCSN(edmx, ignorePersistenceSkip, mockServerUc, context) {
|
|
|
179
198
|
if(context){
|
|
180
199
|
context.oDataVersion = "V4";
|
|
181
200
|
}
|
|
182
|
-
|
|
201
|
+
//default namespaces to be present
|
|
202
|
+
if(!context.include_all_namespaces)
|
|
203
|
+
{
|
|
204
|
+
defaultNamespaces = ['edmx'];
|
|
205
|
+
namespaces = namespaces.concat(defaultNamespaces);
|
|
206
|
+
namespaces = namespaces.filter((item,pos) => namespaces.indexOf(item) === pos); //removing duplicate values
|
|
207
|
+
}
|
|
208
|
+
context.namespaces = namespaces;
|
|
209
|
+
return edmxv4CSN.getEdmxv4CSN(edmx, ignorePersistenceSkip, mockServerUc, context);
|
|
183
210
|
}
|
|
184
211
|
return new Promise(function invalidFile(resolve, reject) {
|
|
185
212
|
reject(new Error(messages.INVALID_EDMX_METADATA));
|
|
186
213
|
});
|
|
187
214
|
}
|
|
188
|
-
|
|
189
215
|
async function saveCSNModel(csnDataModel, fileLoc) {
|
|
190
216
|
await fs.write(fileLoc, csnDataModel, "utf-8");
|
|
191
217
|
}
|
package/lib/import/message.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const messages = {
|
|
4
|
-
INVALID_EDMX_METADATA:
|
|
5
|
-
INVALID_OUTPUT_FILE:
|
|
6
|
-
MISSING_ENTITIES:
|
|
7
|
-
SPECIFY_INPUT_FILE:
|
|
8
|
-
SPECIFY_OUTPUT_FILE:
|
|
4
|
+
INVALID_EDMX_METADATA: 'The OData model is not valid. Specify the correct OData model.',
|
|
5
|
+
INVALID_OUTPUT_FILE: 'Please provide an output file with one of these [cds, csn, json] file extension.',
|
|
6
|
+
MISSING_ENTITIES: 'There are no entities in the OData model. Specify the correct OData model in the provided file.',
|
|
7
|
+
SPECIFY_INPUT_FILE: 'Specify an input file that exists.',
|
|
8
|
+
SPECIFY_OUTPUT_FILE: 'Specify an output file or correct the output file path provided.',
|
|
9
9
|
MISSING_ENTITY_CONTAINER: 'There is no entity container in the OData model. Specify the correct OData model.',
|
|
10
10
|
MISSING_ENTITY_SETS: 'There are no entity sets in the OData model. Specify the correct OData model.',
|
|
11
|
-
INVALID_INPUT_FILE: 'Input file provided is not valid. Please provide a file with either .edmx or .xml extension.'
|
|
11
|
+
INVALID_INPUT_FILE: 'Input file provided is not valid. Please provide a file with either .edmx or .xml extension.',
|
|
12
|
+
FILE_MODIFIED: 'The file with the same name exists and is been modified. Use --force flag to forcefully overwrite the file.',
|
|
13
|
+
OUTPUT_FILE_MISMATCH: 'Specify the file extension that --as option supports.',
|
|
14
|
+
INVALID_AS_OPTION: 'For --as option, please specify one of these output file format [cds, csn, json].',
|
|
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 "*"'
|
|
12
17
|
};
|
|
13
18
|
|
|
14
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
|
|