@sap/cds-compiler 2.12.0 → 2.15.2
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 +221 -15
- package/bin/cdsc.js +125 -50
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +47 -84
- package/lib/api/options.js +5 -6
- package/lib/api/validate.js +6 -11
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +114 -18
- package/lib/base/messages.js +101 -90
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +177 -123
- package/lib/checks/annotationsOData.js +12 -33
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +6 -11
- package/lib/compiler/assert-consistency.js +6 -3
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +19 -6
- package/lib/compiler/checks.js +23 -60
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1151 -0
- package/lib/compiler/extend.js +1000 -0
- package/lib/compiler/finalize-parse-cdl.js +237 -0
- package/lib/compiler/index.js +107 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1227 -0
- package/lib/compiler/propagator.js +114 -46
- package/lib/compiler/resolve.js +1521 -0
- package/lib/compiler/shared.js +126 -65
- package/lib/compiler/tweak-assocs.js +535 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -24
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +219 -100
- package/lib/edm/edm.js +302 -230
- package/lib/edm/edmPreprocessor.js +554 -419
- package/lib/edm/edmUtils.js +138 -44
- package/lib/gen/Dictionary.json +100 -19
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5765 -4480
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +15 -3
- package/lib/json/to-csn.js +126 -68
- package/lib/language/docCommentParser.js +4 -4
- package/lib/language/genericAntlrParser.js +123 -5
- package/lib/language/language.g4 +355 -156
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +486 -59
- package/lib/main.js +41 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +252 -156
- package/lib/model/csnUtils.js +384 -297
- package/lib/model/enrichCsn.js +71 -29
- package/lib/model/revealInternalProperties.js +29 -8
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +23 -18
- package/lib/optionProcessor.js +63 -26
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +897 -947
- package/lib/render/toHdbcds.js +205 -257
- package/lib/render/toSql.js +264 -225
- package/lib/render/utils/common.js +136 -25
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +3 -1
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +104 -306
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +58 -53
- package/lib/transform/db/expansion.js +60 -33
- package/lib/transform/db/flattening.js +582 -104
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +66 -13
- package/lib/transform/db/views.js +11 -7
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +109 -208
- package/lib/transform/forOdataNew.js +59 -212
- package/lib/transform/localized.js +46 -26
- package/lib/transform/odata/toFinalBaseType.js +85 -11
- package/lib/transform/odata/typesExposure.js +147 -199
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +44 -33
- package/lib/transform/translateAssocsToJoins.js +3 -20
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +172 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -290
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
- package/lib/transform/universalCsnEnricher.js +0 -237
|
@@ -74,39 +74,39 @@ function createOptionProcessor() {
|
|
|
74
74
|
*/
|
|
75
75
|
function command(cmdString) {
|
|
76
76
|
/** @type {object} */
|
|
77
|
-
const
|
|
77
|
+
const cmd = {
|
|
78
78
|
options: {},
|
|
79
79
|
positionalArguments: [],
|
|
80
|
-
option,
|
|
80
|
+
option: commandOption,
|
|
81
81
|
positionalArgument: (argumentDefinition) => {
|
|
82
|
-
_setPositionalArguments(argumentDefinition,
|
|
83
|
-
return
|
|
82
|
+
_setPositionalArguments(argumentDefinition, cmd.positionalArguments);
|
|
83
|
+
return cmd;
|
|
84
84
|
},
|
|
85
|
-
help,
|
|
85
|
+
help: commandHelp,
|
|
86
86
|
..._parseCommandString(cmdString)
|
|
87
87
|
};
|
|
88
|
-
if (optionProcessor.commands[
|
|
89
|
-
throw new Error(`Duplicate assignment for long command ${
|
|
88
|
+
if (optionProcessor.commands[cmd.longName]) {
|
|
89
|
+
throw new Error(`Duplicate assignment for long command ${cmd.longName}`);
|
|
90
90
|
}
|
|
91
|
-
optionProcessor.commands[
|
|
91
|
+
optionProcessor.commands[cmd.longName] = cmd;
|
|
92
92
|
|
|
93
|
-
if (
|
|
94
|
-
if (optionProcessor.commands[
|
|
95
|
-
throw new Error(`Duplicate assignment for short command ${
|
|
93
|
+
if (cmd.shortName) {
|
|
94
|
+
if (optionProcessor.commands[cmd.shortName]) {
|
|
95
|
+
throw new Error(`Duplicate assignment for short command ${cmd.shortName}`);
|
|
96
96
|
}
|
|
97
|
-
optionProcessor.commands[
|
|
97
|
+
optionProcessor.commands[cmd.shortName] = cmd;
|
|
98
98
|
}
|
|
99
|
-
return
|
|
99
|
+
return cmd;
|
|
100
100
|
|
|
101
101
|
// Command API: Define a command option
|
|
102
|
-
function
|
|
103
|
-
return _addOption(
|
|
102
|
+
function commandOption(optString, validValues, options) {
|
|
103
|
+
return _addOption(cmd, optString, validValues, options);
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// Command API: Define the command help text
|
|
107
|
-
function
|
|
108
|
-
|
|
109
|
-
return
|
|
107
|
+
function commandHelp(text) {
|
|
108
|
+
cmd.helpText = text;
|
|
109
|
+
return cmd;
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -156,33 +156,33 @@ function createOptionProcessor() {
|
|
|
156
156
|
* @private
|
|
157
157
|
* @see option()
|
|
158
158
|
*/
|
|
159
|
-
function _addOption(
|
|
159
|
+
function _addOption(cmd, optString, validValues, options) {
|
|
160
160
|
const opt = _parseOptionString(optString, validValues);
|
|
161
161
|
Object.assign(opt, options);
|
|
162
162
|
|
|
163
|
-
if (
|
|
163
|
+
if (cmd.options[opt.longName]) {
|
|
164
164
|
throw new Error(`Duplicate assignment for long option ${opt.longName}`);
|
|
165
165
|
} else if (optionProcessor.options[opt.longName]) {
|
|
166
166
|
// This path is only taken if optString is for commands
|
|
167
167
|
optionProcessor.optionClashes.push({
|
|
168
168
|
option: opt.longName,
|
|
169
|
-
description: `Command '${
|
|
169
|
+
description: `Command '${cmd.longName}' has option clash with general options for: ${opt.longName}`
|
|
170
170
|
});
|
|
171
171
|
}
|
|
172
|
-
|
|
172
|
+
cmd.options[opt.longName] = opt;
|
|
173
173
|
if (opt.shortName) {
|
|
174
|
-
if (
|
|
174
|
+
if (cmd.options[opt.shortName]) {
|
|
175
175
|
throw new Error(`Duplicate assignment for short option ${opt.shortName}`);
|
|
176
176
|
} else if (optionProcessor.options[opt.shortName]) {
|
|
177
177
|
// This path is only taken if optString is for commands
|
|
178
178
|
optionProcessor.optionClashes.push({
|
|
179
179
|
option: opt.shortName,
|
|
180
|
-
description: `Command '${
|
|
180
|
+
description: `Command '${cmd.longName}' has option clash with general options for: ${opt.shortName}`
|
|
181
181
|
});
|
|
182
182
|
}
|
|
183
|
-
|
|
183
|
+
cmd.options[opt.shortName] = opt;
|
|
184
184
|
}
|
|
185
|
-
return
|
|
185
|
+
return cmd;
|
|
186
186
|
}
|
|
187
187
|
|
|
188
188
|
// Internal: Parse one command string like "F, toFoo". Return an object like this
|
|
@@ -339,47 +339,12 @@ function createOptionProcessor() {
|
|
|
339
339
|
// No more options after '--'
|
|
340
340
|
seenDashDash = true;
|
|
341
341
|
}
|
|
342
|
+
else if (!seenDashDash && arg.startsWith('--')) {
|
|
343
|
+
i += processOption(i);
|
|
344
|
+
}
|
|
342
345
|
else if (!seenDashDash && arg.startsWith('-')) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
const opt = optionProcessor.commands[result.command].options[arg];
|
|
346
|
-
if (opt) {
|
|
347
|
-
// Found as a command option
|
|
348
|
-
i += processOption(i, opt, result.command);
|
|
349
|
-
} else {
|
|
350
|
-
// No command option, try general options as fallback
|
|
351
|
-
const opt = optionProcessor.options[arg];
|
|
352
|
-
if (opt) {
|
|
353
|
-
i += processOption(i, opt, false);
|
|
354
|
-
} else {
|
|
355
|
-
// Not found at all, put into unknownOptions if it is an option
|
|
356
|
-
// for another cdsc command.
|
|
357
|
-
// We dig into the other cdsc commands in order to check if
|
|
358
|
-
// the option expects a parameter and if so to take the next argument as a value
|
|
359
|
-
if (Object.keys(optionProcessor.commands).some(cmd => optionProcessor.commands[cmd].options[arg])) {
|
|
360
|
-
const cmd = optionProcessor.commands[
|
|
361
|
-
Object.keys(optionProcessor.commands).find(cmd => optionProcessor.commands[cmd].options[arg])
|
|
362
|
-
];
|
|
363
|
-
i += processOption(i, cmd.options[arg], optionProcessor.commands[result.command], true);
|
|
364
|
-
} else { // still add it to the unknownOptions
|
|
365
|
-
result.unknownOptions.push(`Unknown option "${arg}" for the command "${result.command}"`);
|
|
366
|
-
// if the next argument looks like an argument for this unknown option => skip it
|
|
367
|
-
if ((i + 1) < argv.length && !argv[i + 1].match('(^[.-])|[.](csn|cds|json)$'))
|
|
368
|
-
i++;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
} else {
|
|
373
|
-
// We don't have a command
|
|
374
|
-
const opt = optionProcessor.options[arg];
|
|
375
|
-
if (opt) {
|
|
376
|
-
// Found as a general option
|
|
377
|
-
i += processOption(i, opt, false);
|
|
378
|
-
} else {
|
|
379
|
-
// Not found, complain
|
|
380
|
-
result.unknownOptions.push(`Unknown option "${arg}"`);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
346
|
+
splitSingleLetterOption(argv, i); // `-ab` -> `-a -b`
|
|
347
|
+
i += processOption(i);
|
|
383
348
|
}
|
|
384
349
|
else {
|
|
385
350
|
// Command or arg
|
|
@@ -407,7 +372,11 @@ function createOptionProcessor() {
|
|
|
407
372
|
const missingArg = getCurrentPositionArguments().find((arg) => arg.required && !result.args[arg.name]);
|
|
408
373
|
if (missingArg) {
|
|
409
374
|
const forCommand = result.command ? ` for '${ result.command }'` : '';
|
|
410
|
-
|
|
375
|
+
const errorMsg = `Missing positional argument${forCommand}: <${missingArg.name}${missingArg.isDynamic ? '...' : ''}>`;
|
|
376
|
+
if (forCommand)
|
|
377
|
+
result.cmdErrors.push(errorMsg)
|
|
378
|
+
else
|
|
379
|
+
result.errors.push(errorMsg)
|
|
411
380
|
}
|
|
412
381
|
|
|
413
382
|
return result;
|
|
@@ -448,57 +417,127 @@ function createOptionProcessor() {
|
|
|
448
417
|
|
|
449
418
|
// (Note that this works on 'argv' and 'result' from above).
|
|
450
419
|
// Process 'argv[i]' as an option.
|
|
451
|
-
// Check the option definition
|
|
420
|
+
// Check the option definition to see if a parameter is expected.
|
|
452
421
|
// If so, take it (complain if one is found in 'argv').
|
|
453
422
|
// Populate 'result.options' with the result. Return the number params found (0 or 1).
|
|
454
|
-
function processOption(i
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
423
|
+
function processOption(i) {
|
|
424
|
+
const arg = argv[i];
|
|
425
|
+
let currentCommand = result.command;
|
|
426
|
+
|
|
427
|
+
// First check top-level options
|
|
428
|
+
let currentOption = optionProcessor.options[arg];
|
|
429
|
+
if (currentCommand) {
|
|
430
|
+
// If there is a command and it has an option that overrides it, use it instead.
|
|
431
|
+
const cmdOpt = optionProcessor.commands[currentCommand].options[arg];
|
|
432
|
+
if (cmdOpt)
|
|
433
|
+
currentOption = cmdOpt;
|
|
434
|
+
else if (currentOption)
|
|
435
|
+
// Otherwise, if there exist a top-level option, set 'command' to null.
|
|
436
|
+
currentCommand = null;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (!currentOption)
|
|
440
|
+
return reportUnknown();
|
|
441
|
+
|
|
442
|
+
if (!currentOption.param) {
|
|
443
|
+
setCurrentOption(true);
|
|
444
|
+
return 0;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const param = paramForOption(currentOption);
|
|
448
|
+
if (param === null)
|
|
449
|
+
return 0;
|
|
450
|
+
setCurrentOption(param);
|
|
451
|
+
return 1;
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Report that an option is unknown. If the option exists for other
|
|
455
|
+
* commands or if the next argument looks like a param, return 1,
|
|
456
|
+
* otherwise 0, indicating how many argv fields have been consumed.
|
|
457
|
+
*
|
|
458
|
+
* @returns {number}
|
|
459
|
+
*/
|
|
460
|
+
function reportUnknown() {
|
|
461
|
+
if (currentCommand)
|
|
462
|
+
result.unknownOptions.push(`Unknown option "${arg}" for the command "${currentCommand}"`);
|
|
463
|
+
else
|
|
464
|
+
result.unknownOptions.push(`Unknown option "${arg}"`);
|
|
465
|
+
|
|
466
|
+
if (currentCommand) {
|
|
467
|
+
// Not found at all. We dig into the other cdsc commands in order to check if
|
|
468
|
+
// the option expects a parameter and if so to take the next argument as a value
|
|
469
|
+
const otherCmd = Object.keys(optionProcessor.commands).find(cmd => optionProcessor.commands[cmd].options[arg]);
|
|
470
|
+
const otherCmdOpt = otherCmd && optionProcessor.commands[otherCmd].options[arg];
|
|
471
|
+
if (otherCmdOpt && hasParamForUnknown(otherCmdOpt)) {
|
|
472
|
+
return 1
|
|
467
473
|
}
|
|
468
|
-
return 0;
|
|
469
474
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
const value = argv[i + 1];
|
|
473
|
-
const shortOption = opt.shortName ? `${opt.shortName}, ` : ''
|
|
474
|
-
if (command) {
|
|
475
|
-
// if an unknown option for a command => add it to the array and warn about
|
|
476
|
-
if (unknownOption) {
|
|
477
|
-
result.unknownOptions.push(`Unknown option "${argv[i]}" for the command "${command.longName}"`);
|
|
478
|
-
} else {
|
|
479
|
-
result.options[command][opt.camelName] = value;
|
|
480
|
-
if (!isValidOptionValue(opt, value)) {
|
|
481
|
-
result.cmdErrors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
} else {
|
|
485
|
-
result.options[opt.camelName] = value;
|
|
486
|
-
if (!isValidOptionValue(opt, value)) {
|
|
487
|
-
result.errors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
475
|
+
|
|
476
|
+
if (hasParamForUnknown(null))
|
|
490
477
|
return 1;
|
|
478
|
+
|
|
479
|
+
return 0;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function setCurrentOption(val) {
|
|
483
|
+
if (currentCommand) {
|
|
484
|
+
if (!result.options[currentCommand])
|
|
485
|
+
result.options[currentCommand] = {};
|
|
486
|
+
result.options[currentCommand][currentOption.camelName] = val;
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
result.options[currentOption.camelName] = val;
|
|
491
490
|
}
|
|
492
491
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
492
|
+
|
|
493
|
+
function reportMissingParam(opt) {
|
|
494
|
+
let error = `Missing param "${opt.param}" for option "${opt.shortName ? opt.shortName + ', ': ''}${opt.longName}"`;
|
|
495
|
+
if (currentCommand) {
|
|
496
|
+
error = `${error} of command "${currentCommand}"`;
|
|
497
|
+
result.cmdErrors.push(error);
|
|
498
|
+
} else {
|
|
499
|
+
result.errors.push(error);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function reportInvalidValue(opt, value) {
|
|
504
|
+
const shortOption = opt.shortName ? `${opt.shortName}, ` : ''
|
|
505
|
+
const errors = currentCommand ? result.cmdErrors : result.errors;
|
|
506
|
+
errors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Get the value for the option's parameter. If the option does not require one,
|
|
511
|
+
* returns `null`. Reports missing parameters and invalid values.
|
|
512
|
+
*
|
|
513
|
+
* @returns {null|*}
|
|
514
|
+
*/
|
|
515
|
+
function paramForOption(opt, reportMissing = true) {
|
|
516
|
+
if (i + 1 >= argv.length || argv[i + 1].startsWith('-')) {
|
|
517
|
+
if (reportMissing)
|
|
518
|
+
reportMissingParam(opt)
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const value = argv[i + 1];
|
|
523
|
+
if (!isValidOptionValue(opt, value) && reportMissing) {
|
|
524
|
+
reportInvalidValue(opt, value);
|
|
525
|
+
}
|
|
526
|
+
return value;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* Returns true if:
|
|
531
|
+
* - we didn't find an option (opt === null) _or_
|
|
532
|
+
* - we found an option and it requires a param
|
|
533
|
+
* _and_ if the next arg looks like an argument.
|
|
534
|
+
*
|
|
535
|
+
* @param {object|null} opt
|
|
536
|
+
* @returns {boolean}
|
|
537
|
+
*/
|
|
538
|
+
function hasParamForUnknown(opt) {
|
|
539
|
+
return ((!opt || opt.param) && (i + 1) < argv.length && !argv[i + 1].match('(^[.-])|[.](csn|cdl|cds|json)$'));
|
|
500
540
|
}
|
|
501
|
-
return 0;
|
|
502
541
|
}
|
|
503
542
|
}
|
|
504
543
|
|
|
@@ -506,16 +545,16 @@ function createOptionProcessor() {
|
|
|
506
545
|
// If 'command' is supplied, check only 'options.command', otherwise check
|
|
507
546
|
// only top-level options
|
|
508
547
|
// Return an array of complaints (possibly empty)
|
|
509
|
-
function verifyOptions(options,
|
|
548
|
+
function verifyOptions(options, commandName = undefined, silent = false) {
|
|
510
549
|
const result = [];
|
|
511
550
|
let opts;
|
|
512
551
|
|
|
513
|
-
if((options.betaMode || options.beta) && !options.testMode && !silent) {
|
|
552
|
+
if ((options.betaMode || options.beta) && !options.testMode && !silent) {
|
|
514
553
|
const mode = options.beta ? 'beta' : 'beta-mode';
|
|
515
554
|
result.push(`Option --${mode} was used. This option should not be used in productive scenarios!`)
|
|
516
555
|
}
|
|
517
556
|
|
|
518
|
-
if(options) {
|
|
557
|
+
if (options) {
|
|
519
558
|
[
|
|
520
559
|
'defaultBinaryLength', 'defaultStringLength',
|
|
521
560
|
/*'length', 'precision', 'scale'*/
|
|
@@ -528,13 +567,13 @@ function createOptionProcessor() {
|
|
|
528
567
|
});
|
|
529
568
|
}
|
|
530
569
|
|
|
531
|
-
if (
|
|
532
|
-
const cmd = optionProcessor.commands[
|
|
570
|
+
if (commandName) {
|
|
571
|
+
const cmd = optionProcessor.commands[commandName];
|
|
533
572
|
if (!cmd) {
|
|
534
|
-
throw new Error(`Expected existing command: "${
|
|
573
|
+
throw new Error(`Expected existing command: "${cmd}"`);
|
|
535
574
|
}
|
|
536
575
|
opts = cmd.options;
|
|
537
|
-
options = options[
|
|
576
|
+
options = options[cmd] || {};
|
|
538
577
|
if (typeof options === 'boolean') {
|
|
539
578
|
// Special case: command without options
|
|
540
579
|
options = {};
|
|
@@ -548,12 +587,12 @@ function createOptionProcessor() {
|
|
|
548
587
|
let error;
|
|
549
588
|
if (!opt) {
|
|
550
589
|
// Don't report commands in top-level options
|
|
551
|
-
if ((
|
|
552
|
-
error = `Unknown option "${
|
|
590
|
+
if ((commandName || !optionProcessor.commands[camelName]) && !silent) {
|
|
591
|
+
error = `Unknown option "${commandName ? commandName + '.' : ''}${camelName}"`;
|
|
553
592
|
}
|
|
554
593
|
} else {
|
|
555
594
|
const param = options[camelName];
|
|
556
|
-
error = verifyOptionParam(param, opt,
|
|
595
|
+
error = verifyOptionParam(param, opt, commandName ? commandName + '.' : '');
|
|
557
596
|
}
|
|
558
597
|
if (error) {
|
|
559
598
|
result.push(error);
|
|
@@ -596,15 +635,30 @@ function createOptionProcessor() {
|
|
|
596
635
|
|
|
597
636
|
// Return an array of unique camelNames of the options for the specified command
|
|
598
637
|
// If invalid command -> an empty array
|
|
599
|
-
function camelOptionsForCommand(
|
|
600
|
-
if (!
|
|
638
|
+
function camelOptionsForCommand(cmd) {
|
|
639
|
+
if (!cmd || !optionProcessor.commands[cmd])
|
|
601
640
|
return []
|
|
602
|
-
|
|
641
|
+
cmd = optionProcessor.commands[cmd];
|
|
603
642
|
const names = Object.keys(cmd.options).map(name => cmd.options[name].camelName);
|
|
604
643
|
return [...new Set(names)];
|
|
605
644
|
}
|
|
606
645
|
}
|
|
607
646
|
|
|
647
|
+
/**
|
|
648
|
+
* Splits `-abc` into `-a -b -c`. Does this in-place on argv.
|
|
649
|
+
*
|
|
650
|
+
* @param {string[]} argv Argument array
|
|
651
|
+
* @param {number} i Current option index.
|
|
652
|
+
*/
|
|
653
|
+
function splitSingleLetterOption(argv, i) {
|
|
654
|
+
const arg = argv[i];
|
|
655
|
+
if (arg.length > 2) { // must be at least `-ab`.
|
|
656
|
+
const rest = argv.slice(i + 1);
|
|
657
|
+
argv.length = i; // trim array
|
|
658
|
+
argv.push(...arg.split('').slice(1).map(a => `-${a}`), ...rest);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
608
662
|
/**
|
|
609
663
|
* Return a camelCase name "fooBar" for a long option "--foo-bar"
|
|
610
664
|
*/
|
|
@@ -13,42 +13,22 @@
|
|
|
13
13
|
* @param {CSN.Element} member Member to be checked
|
|
14
14
|
*/
|
|
15
15
|
function checkCoreMediaTypeAllowence(member) {
|
|
16
|
-
const allowedCoreMediaTypes =
|
|
17
|
-
'cds.String',
|
|
18
|
-
'cds.LargeString',
|
|
19
|
-
'cds.hana.VARCHAR',
|
|
20
|
-
'cds.hana.CHAR',
|
|
21
|
-
'cds.Binary',
|
|
22
|
-
'cds.LargeBinary',
|
|
23
|
-
'cds.hana.CLOB',
|
|
24
|
-
'cds.hana.BINARY',
|
|
25
|
-
|
|
26
|
-
if (member['@Core.MediaType'] && member.type && !
|
|
16
|
+
const allowedCoreMediaTypes = {
|
|
17
|
+
'cds.String': 1,
|
|
18
|
+
'cds.LargeString': 1,
|
|
19
|
+
'cds.hana.VARCHAR': 1,
|
|
20
|
+
'cds.hana.CHAR': 1,
|
|
21
|
+
'cds.Binary': 1,
|
|
22
|
+
'cds.LargeBinary': 1,
|
|
23
|
+
'cds.hana.CLOB': 1,
|
|
24
|
+
'cds.hana.BINARY': 1,
|
|
25
|
+
};
|
|
26
|
+
if (member['@Core.MediaType'] && member.type && !(this.csnUtils.getFinalBaseType(member.type) in allowedCoreMediaTypes)) {
|
|
27
27
|
this.warning(null, member.$path, { names: [ 'Edm.String', 'Edm.Binary' ] },
|
|
28
28
|
'Element annotated with “@Core.MediaType” should be of a type mapped to $(NAMES)');
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/**
|
|
33
|
-
* Make sure only one element in a definition is annotated with `@Core.MediaType`
|
|
34
|
-
* This is only OData V2 relevant.
|
|
35
|
-
*
|
|
36
|
-
* @param {CSN.Artifact} artifact Definition to be checked
|
|
37
|
-
* @param {string} artifactName The name of the artifact
|
|
38
|
-
*/
|
|
39
|
-
function checkForMultipleCoreMediaTypes(artifact, artifactName) {
|
|
40
|
-
if (!this.csnUtils.getServiceName(artifactName))
|
|
41
|
-
return;
|
|
42
|
-
if (this.options.toOdata && this.options.toOdata.version === 'v2' && artifact.elements) {
|
|
43
|
-
const mediaTypeElementsNames = Object.keys(artifact.elements)
|
|
44
|
-
.filter(elementName => artifact.elements[elementName]['@Core.MediaType']);
|
|
45
|
-
if (mediaTypeElementsNames.length > 1) {
|
|
46
|
-
this.error(null, artifact.$path, { names: mediaTypeElementsNames },
|
|
47
|
-
`Multiple elements $(NAMES) annotated with “@Core.MediaType”, OData V2 allows only one`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
32
|
/**
|
|
53
33
|
* Check if `@Aggregation.default` is assigned together with `@Analytics.Measure`
|
|
54
34
|
*
|
|
@@ -82,13 +62,12 @@ function checkAtSapAnnotations(node) {
|
|
|
82
62
|
function checkReadOnlyAndInsertOnly(artifact, artifactName) {
|
|
83
63
|
if (!this.csnUtils.getServiceName(artifactName))
|
|
84
64
|
return;
|
|
85
|
-
if (artifact.kind
|
|
65
|
+
if (artifact.kind === 'entity' && artifact['@readonly'] && artifact['@insertonly'])
|
|
86
66
|
this.warning(null, artifact.$path, 'Annotations “@readonly” and “@insertonly” can\'t be assigned in combination');
|
|
87
67
|
}
|
|
88
68
|
|
|
89
69
|
module.exports = {
|
|
90
70
|
checkCoreMediaTypeAllowence,
|
|
91
|
-
checkForMultipleCoreMediaTypes,
|
|
92
71
|
checkAnalytics,
|
|
93
72
|
checkAtSapAnnotations,
|
|
94
73
|
checkReadOnlyAndInsertOnly,
|
package/lib/checks/arrayOfs.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { forEachMemberRecursively } = require('../model/csnUtils');
|
|
4
|
-
|
|
5
3
|
// Only to be used with validator.js - a correct `this` value needs to be provided!
|
|
6
4
|
|
|
7
5
|
/**
|
|
@@ -43,35 +41,4 @@ function validateAssociationsInItems(member) {
|
|
|
43
41
|
}
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
* Check that there are no .items containing .items.
|
|
48
|
-
*
|
|
49
|
-
* @param {CSN.Artifact} art Artifact
|
|
50
|
-
* @param {string} artName Name of the artifact
|
|
51
|
-
*/
|
|
52
|
-
function checkChainedArray(art, artName) {
|
|
53
|
-
if (!this.csnUtils.getServiceName(artName))
|
|
54
|
-
return;
|
|
55
|
-
checkIfItemsOfItems.bind(this)(art);
|
|
56
|
-
forEachMemberRecursively(art, checkIfItemsOfItems.bind(this));
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
*
|
|
60
|
-
* @param {object} construct the construct to be checked
|
|
61
|
-
*/
|
|
62
|
-
function checkIfItemsOfItems(construct) {
|
|
63
|
-
const constructType = this.csnUtils.effectiveType(construct);
|
|
64
|
-
if (constructType.items) {
|
|
65
|
-
if (constructType.items.items) {
|
|
66
|
-
this.error('chained-array-of', construct.$path, '"Array of"/"many" must not be chained with another "array of"/"many" inside a service');
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const itemsType = this.csnUtils.effectiveType(constructType.items);
|
|
71
|
-
if (itemsType.items)
|
|
72
|
-
this.error('chained-array-of', construct.$path, '"Array of"/"many" must not be chained with another "array of"/"many" inside a service');
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
module.exports = { validateAssociationsInItems, checkChainedArray };
|
|
44
|
+
module.exports = { validateAssociationsInItems };
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
function validateCdsPersistenceAnnotation(artifact, artifactName, prop, path) {
|
|
14
14
|
if (artifact.kind === 'entity') {
|
|
15
15
|
// filter for 'table', 'udf', 'calcview' === true
|
|
16
|
-
const
|
|
16
|
+
const persistenceAnnos = [ '@cds.persistence.table', '@cds.persistence.udf', '@cds.persistence.calcview' ];
|
|
17
|
+
const TableUdfCv = Object.keys(artifact).filter(p => persistenceAnnos.includes(p) && artifact[p]);
|
|
17
18
|
if (TableUdfCv.length > 1)
|
|
18
19
|
this.error(null, path, `Annotations ${ TableUdfCv.join(', ') } can't be used in combination`);
|
|
19
20
|
}
|
package/lib/checks/enricher.js
CHANGED
|
@@ -30,6 +30,7 @@ function enrichCsn( csn ) {
|
|
|
30
30
|
type: simpleRef,
|
|
31
31
|
target: simpleRef,
|
|
32
32
|
includes: simpleRef,
|
|
33
|
+
columns,
|
|
33
34
|
// Annotations are ignored.
|
|
34
35
|
'@': () => { /* ignore annotations */ },
|
|
35
36
|
};
|
|
@@ -40,7 +41,7 @@ function enrichCsn( csn ) {
|
|
|
40
41
|
cleanupCallbacks = [];
|
|
41
42
|
};
|
|
42
43
|
|
|
43
|
-
const { inspectRef, artifactRef } = csnRefs( csn );
|
|
44
|
+
const { inspectRef, artifactRef, getElement } = csnRefs( csn );
|
|
44
45
|
const csnPath = [];
|
|
45
46
|
if (csn.definitions)
|
|
46
47
|
dictionary( csn, 'definitions', csn.definitions );
|
|
@@ -82,6 +83,21 @@ function enrichCsn( csn ) {
|
|
|
82
83
|
csnPath.pop();
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
87
|
+
function columns( parent, prop, node ) {
|
|
88
|
+
// Establish the link relationships
|
|
89
|
+
parent[prop].forEach((column) => {
|
|
90
|
+
const element = getElement(column);
|
|
91
|
+
if (element) {
|
|
92
|
+
setProp(column, '_element', element);
|
|
93
|
+
cleanupCallbacks.push(() => delete column._element);
|
|
94
|
+
setProp(element, '_column', column);
|
|
95
|
+
cleanupCallbacks.push(() => delete element._column);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
standard(parent, prop, node);
|
|
99
|
+
}
|
|
100
|
+
|
|
85
101
|
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
86
102
|
function simpleRef( node, prop ) {
|
|
87
103
|
setProp(node, '$path', [ ...csnPath ]);
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// Only to be used with validator.js - a correct this value needs to be provided!
|
|
4
4
|
|
|
5
|
+
const { ModelError } = require('../base/error');
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* Assert that targets of associations and compositions are entities.
|
|
7
9
|
*
|
|
@@ -22,7 +24,7 @@ function invalidTarget(member) {
|
|
|
22
24
|
if (mem.target) {
|
|
23
25
|
const target = this.csn.definitions[mem.target];
|
|
24
26
|
if (!target)
|
|
25
|
-
throw new
|
|
27
|
+
throw new ModelError(`Expected target ${ mem.target }`);
|
|
26
28
|
if (target.kind !== 'entity') {
|
|
27
29
|
const isAssoc = this.csnUtils.getFinalBaseType(member.type) !== 'cds.Composition';
|
|
28
30
|
this.error(
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
const { ModelError } = require('../base/error');
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Trigger a recompilation in case of an association without .keys and without .on
|
|
6
8
|
*
|
|
@@ -10,7 +12,7 @@
|
|
|
10
12
|
*/
|
|
11
13
|
function managedWithoutKeys(member, memberName, prop) {
|
|
12
14
|
if (prop === 'elements' && member.target && !member.keys && !member.on) { // trigger recompilation
|
|
13
|
-
throw new
|
|
15
|
+
throw new ModelError('Expected association to have either an on-condition or foreign keys.');
|
|
14
16
|
}
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -29,7 +29,7 @@ function validateSelectItems(query) {
|
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
// .call() with 'this' to ensure we have access to the options
|
|
32
|
-
|
|
32
|
+
rejectManagedAssociationsAndStructuresForHdbcdsNames.call(this, SELECT, SELECT.$path);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
|
|
@@ -41,10 +41,10 @@ function validateSelectItems(query) {
|
|
|
41
41
|
* @param {CSN.Artifact} queryArtifact the query artifact which should be checked
|
|
42
42
|
* @param {CSN.Path} artifactPath the path to that artifact
|
|
43
43
|
*/
|
|
44
|
-
function
|
|
44
|
+
function rejectManagedAssociationsAndStructuresForHdbcdsNames(queryArtifact, artifactPath) {
|
|
45
45
|
if (this.options.transformation === 'hdbcds' && this.options.sqlMapping === 'hdbcds') {
|
|
46
46
|
forEachGeneric(queryArtifact, 'elements', (selectItem, elemName, prop, elementPath) => {
|
|
47
|
-
if (this.csnUtils.
|
|
47
|
+
if (this.csnUtils.isManagedAssociation(selectItem))
|
|
48
48
|
this.error('query-unexpected-assoc-hdbcds', elementPath);
|
|
49
49
|
if (this.csnUtils.isStructured(selectItem))
|
|
50
50
|
this.error('query-unexpected-structure-hdbcds', elementPath);
|
|
@@ -52,4 +52,4 @@ function rejectManagedAssociationsAndStructuresForHdbcsNames(queryArtifact, arti
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
module.exports = { validateSelectItems,
|
|
55
|
+
module.exports = { validateSelectItems, rejectManagedAssociationsAndStructuresForHdbcdsNames };
|