@sap/xsodata 7.4.4 → 7.5.5
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 +27 -0
- package/lib/db/connect.js +8 -3
- package/lib/db/dbSegment.js +89 -3
- package/lib/grammars/xsodata.peg +10 -1
- package/lib/model/annotationFactory.js +3 -0
- package/lib/model/metadataReader.js +13 -0
- package/lib/parsers/peg_xsodata_parser.js +192 -146
- package/lib/serializer/json.js +37 -8
- package/lib/sql/createGetStatements.js +23 -6
- package/lib/sql/dataCollector2.js +11 -0
- package/lib/sql/sqlStatement.js +3 -2
- package/lib/uri/oDataUriParser.js +8 -1
- package/lib/uri/resourcePathParser.js +6 -0
- package/lib/utils/logger.js +3 -2
- package/lib/utils/typedObjects.js +8 -0
- package/lib/xsodata.js +15 -0
- package/package.json +18 -23
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,33 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|
|
8
8
|
|
|
9
9
|
## Unreleased
|
|
10
10
|
|
|
11
|
+
## [7.5.5] - 2022-04-21
|
|
12
|
+
|
|
13
|
+
* On calc view: Stable sort order with $skip and $top usage for subsequent requests via "order by" of key fields in SQL statement
|
|
14
|
+
|
|
15
|
+
## [7.5.4] - 2022-04-21
|
|
16
|
+
|
|
17
|
+
Build infrastructure fix
|
|
18
|
+
|
|
19
|
+
## [7.5.1] - [7.5.3] - 2022-04-20
|
|
20
|
+
|
|
21
|
+
Build infrastructure fixes
|
|
22
|
+
|
|
23
|
+
## [7.5.0] - 2022-03-22
|
|
24
|
+
|
|
25
|
+
* Support of node version 16
|
|
26
|
+
* Update module dependencies
|
|
27
|
+
* Update of system query options and xsodata-settings help text
|
|
28
|
+
* Minor changes: Error reporting
|
|
29
|
+
|
|
30
|
+
## [7.4.5] - 2021-12-15
|
|
31
|
+
|
|
32
|
+
* Suppress $metadata annotation <code>sap:aggregation-role="dimension"</code> on calculation view property if it is used as description property referenced by annotation <code>sap:text</code> by another property of the calulation view. The annotation is only supressed if the corresponding xsodata-file has <code>settings</code> containing this: <code>noDimensionAnnoOnTextProperty true;</code>
|
|
33
|
+
* Improvements on URI parsing errors:
|
|
34
|
+
* reporting URI resource path character position on parsing error, if possible
|
|
35
|
+
* Invalid navigation property on <code>$expand</code> system query option returns 400 (Bad Request) instead of 500 (Internal Server Error)
|
|
36
|
+
* Removed support for NodeJS version 10
|
|
37
|
+
|
|
11
38
|
## [7.4.4] - 2021-07-20
|
|
12
39
|
|
|
13
40
|
* HANA client API usage changed: 'execQuery' used for read requests, change requests remain on 'exec'
|
package/lib/db/connect.js
CHANGED
|
@@ -238,10 +238,10 @@ function _connectInternal(context, asyncDone) {
|
|
|
238
238
|
exports.connect = function (context, asyncDone) {
|
|
239
239
|
context.db = context.db || {};
|
|
240
240
|
if (context.db.client && (context.db.connectionInitialized === true)) {
|
|
241
|
-
context.logger.
|
|
241
|
+
context.logger.info('connect - db', 'connect already done');
|
|
242
242
|
return asyncDone(null, context);
|
|
243
243
|
}
|
|
244
|
-
context.logger.
|
|
244
|
+
context.logger.info('connect - db', 'connect');
|
|
245
245
|
|
|
246
246
|
return _connectInternal(context, function (err) {
|
|
247
247
|
if (err) {
|
|
@@ -268,7 +268,12 @@ exports.disconnect = function (context, cb) {
|
|
|
268
268
|
if (context.db.openedConnection) {
|
|
269
269
|
context.logger.info('connect - db', 'disconnect done');
|
|
270
270
|
if (context.db.client) {
|
|
271
|
-
context.db.client.end()
|
|
271
|
+
context.db.client.end((err) => {
|
|
272
|
+
if (err) {
|
|
273
|
+
context.logger.error('connect - db', `disconnect failed (callback): ${err}`);
|
|
274
|
+
}
|
|
275
|
+
context.logger.info('connect - db', 'disconnect done (callback)');
|
|
276
|
+
});
|
|
272
277
|
}
|
|
273
278
|
context.db.openedConnection = false;
|
|
274
279
|
return cb();
|
package/lib/db/dbSegment.js
CHANGED
|
@@ -92,6 +92,13 @@ function DbSegment(kind, entityType, nr) {
|
|
|
92
92
|
* @type {Array<String>}
|
|
93
93
|
*/
|
|
94
94
|
this._SelectedPropertiesOrdered = null;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* For CV: (real) key names with alias
|
|
98
|
+
* @type {Array<String,string>}
|
|
99
|
+
*/
|
|
100
|
+
this._aliasedKeyPropertiesOnCalcView = [];
|
|
101
|
+
|
|
95
102
|
/**
|
|
96
103
|
* Store names existing of navigation properties
|
|
97
104
|
* @type {Array<String>}
|
|
@@ -247,7 +254,7 @@ DbSegment.prototype.setRecordFromPostPayload = function (context, record) {
|
|
|
247
254
|
(modifyBy && key === 'MODIFIED_BY') ||
|
|
248
255
|
(modifyAt && key === 'MODIFIED_AT'))
|
|
249
256
|
) {
|
|
250
|
-
throw new BadRequest('The serialized resource has
|
|
257
|
+
throw new BadRequest('The serialized resource has a missing value for member ' + key);
|
|
251
258
|
}
|
|
252
259
|
}
|
|
253
260
|
dbValue = null;
|
|
@@ -336,7 +343,7 @@ DbSegment.prototype.setRecordFromPutPayload = function (context, record) {
|
|
|
336
343
|
// skip optional calc view parameters
|
|
337
344
|
(this.entityType.propertiesMap[property] && this.entityType.propertiesMap[property].MANDATORY === 0)
|
|
338
345
|
)) {
|
|
339
|
-
throw new BadRequest('The serialized resource has
|
|
346
|
+
throw new BadRequest('The serialized resource has a missing value for member ' + property);
|
|
340
347
|
}
|
|
341
348
|
|
|
342
349
|
}
|
|
@@ -593,6 +600,50 @@ DbSegment.prototype.getKeyProperties0123ForSelectAs0123 = function (noTable) {
|
|
|
593
600
|
return ret;
|
|
594
601
|
};
|
|
595
602
|
|
|
603
|
+
DbSegment.prototype.getKeyProperties0123ForSelectOnCalcViewAs0123 = function (noTable) {
|
|
604
|
+
let ret = [];
|
|
605
|
+
let keyNames;
|
|
606
|
+
let keyName;
|
|
607
|
+
let property;
|
|
608
|
+
let selectProperty;
|
|
609
|
+
let propertyType;
|
|
610
|
+
let i;
|
|
611
|
+
let j = -1;
|
|
612
|
+
|
|
613
|
+
keyNames = this.entityType.keyNamesOrdered;
|
|
614
|
+
|
|
615
|
+
for (i = 0; i < keyNames.length; i++) {
|
|
616
|
+
keyName = keyNames[i];
|
|
617
|
+
property = this.entityType.propertiesMap[keyName];
|
|
618
|
+
selectProperty = null;
|
|
619
|
+
|
|
620
|
+
if (property.KIND !== EntityType.entityKind.inputParameters) {
|
|
621
|
+
j = j + 1; // for real key properties use numbering: 0,1,2, ... (skip CV input params)
|
|
622
|
+
propertyType = property.aggregate ? null : property.DATA_TYPE_NAME;
|
|
623
|
+
selectProperty = new sqlStatement.SelectProperty(noTable ? null : this._Alias, keyName, propertyType, j.toString());
|
|
624
|
+
ret.push(selectProperty);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return ret;
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
DbSegment.prototype.setAliasedKeyPropertiesOnCalcView = function(selectProperties) {
|
|
631
|
+
this._aliasedKeyPropertiesOnCalcView = selectProperties;
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
DbSegment.prototype.hasAliasedKeyPropertiesOnCalcView = function() {
|
|
635
|
+
return this._aliasedKeyPropertiesOnCalcView.length !== 0;
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
DbSegment.prototype.getAliasedKeyPropertyOnCalcView = function(propertyName) {
|
|
639
|
+
for (let i = 0; i < this._aliasedKeyPropertiesOnCalcView.length; i++) {
|
|
640
|
+
if (this._aliasedKeyPropertiesOnCalcView[i].property === propertyName) {
|
|
641
|
+
return this._aliasedKeyPropertiesOnCalcView[i];
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return null;
|
|
645
|
+
};
|
|
646
|
+
|
|
596
647
|
/**
|
|
597
648
|
* Returns a selectProperty for usage in the select part of sql statements
|
|
598
649
|
* The property are named "0","1","2",... and have as type the type of the corresponding key ( ordered by position
|
|
@@ -649,7 +700,30 @@ DbSegment.prototype.getKeyProperties0123ForOrderBy = function (noTable) {
|
|
|
649
700
|
return ret;
|
|
650
701
|
};
|
|
651
702
|
|
|
652
|
-
DbSegment.prototype.
|
|
703
|
+
DbSegment.prototype.getKeyProperties0123ForOrderByCalcView = function (noTable) {
|
|
704
|
+
let ret = [];
|
|
705
|
+
let keyName;
|
|
706
|
+
let property;
|
|
707
|
+
let j = -1;
|
|
708
|
+
|
|
709
|
+
let keyNames = this.entityType.keyNamesOrdered;
|
|
710
|
+
|
|
711
|
+
for (let i = 0; i < keyNames.length; i++) {
|
|
712
|
+
|
|
713
|
+
keyName = keyNames[i];
|
|
714
|
+
property = this.entityType.propertiesMap[keyName];
|
|
715
|
+
|
|
716
|
+
if (property.KIND !== EntityType.entityKind.inputParameters) {
|
|
717
|
+
j = j + 1;
|
|
718
|
+
let newSortOrder = new typedObjects.SortOrder(new typedObjects.Property(j.toString(), noTable ? null : this._Alias), 'ASC');
|
|
719
|
+
newSortOrder.setPropertyName(property.COLUMN_NAME);
|
|
720
|
+
ret.push(newSortOrder);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return ret;
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
DbSegment.prototype.getKeyPropertiesNotSelectedForSelect = function (noTable = undefined, inputParameters = null) {
|
|
653
727
|
let ret = [];
|
|
654
728
|
let keyNames;
|
|
655
729
|
let i;
|
|
@@ -659,6 +733,18 @@ DbSegment.prototype.getKeyPropertiesNotSelectedForSelect = function (noTable) {
|
|
|
659
733
|
|
|
660
734
|
keyNames = this.entityType.keyNamesOrdered;
|
|
661
735
|
|
|
736
|
+
// remove input params from key list
|
|
737
|
+
if (inputParameters !== null) {
|
|
738
|
+
for (let inputParameterName in inputParameters) {
|
|
739
|
+
if (inputParameters.hasOwnProperty(inputParameterName)) {
|
|
740
|
+
let indexKeyName = keyNames.indexOf(inputParameterName);
|
|
741
|
+
if (indexKeyName > -1) {
|
|
742
|
+
keyNames.splice(indexKeyName, 1);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
662
748
|
for (i = 0; i < keyNames.length; i++) {
|
|
663
749
|
keyName = keyNames[i];
|
|
664
750
|
key = this.entityType.propertiesMap[keyName];
|
package/lib/grammars/xsodata.peg
CHANGED
|
@@ -613,7 +613,7 @@ settingscontent = sl:( OWS settingsconfig OWS ";")*
|
|
|
613
613
|
return ret;
|
|
614
614
|
}
|
|
615
615
|
settingsconfig = s:(settings_meta_cache / settings_content / settings_enable / settings_support /
|
|
616
|
-
settings_hints / settings_admindata / settings_limits )
|
|
616
|
+
settings_hints / settings_admindata / settings_limits / settings_noDimensionAnnoOnTextProperty )
|
|
617
617
|
{ return s; }
|
|
618
618
|
settings_meta_cache = "metadata" MWS ( "cache.control" / "cache-control") MWS qs:quotedstring
|
|
619
619
|
{ return {
|
|
@@ -635,6 +635,15 @@ settings_enable = "enable" MWS string
|
|
|
635
635
|
{ return { name : "enable", value : s }; }
|
|
636
636
|
settings_support = "support" MWS s:string
|
|
637
637
|
{ return { name : "support", value : s }; }
|
|
638
|
+
|
|
639
|
+
settings_noDimensionAnnoOnTextProperty = "noDimensionAnnoOnTextProperty" MWS s:string
|
|
640
|
+
{
|
|
641
|
+
return {
|
|
642
|
+
name : "noDimensionAnnoOnTextProperty",
|
|
643
|
+
value : s
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
|
|
638
647
|
settings_hints = "hints" MWS hl:("null" / hintlist)
|
|
639
648
|
{
|
|
640
649
|
var value = hl === 'null' ? [] : hl
|
|
@@ -120,6 +120,9 @@ function setPropertyAggregationRole(entityType, property, propertyAnnotations) {
|
|
|
120
120
|
(entityType.kind !== EntityType.entityKind.inputParameters &&
|
|
121
121
|
(entityType.hasAggregates() || entityType.hasMeasureProperties())
|
|
122
122
|
) && entityType.isInBimcDimensionView(property.COLUMN_NAME)) {
|
|
123
|
+
if (property.suppressAnnotationDimension && property.suppressAnnotationDimension === true) {
|
|
124
|
+
return; // property is used as description text for other property: do not add annotation
|
|
125
|
+
}
|
|
123
126
|
propertyAnnotations["sap:aggregation-role"] = "dimension";
|
|
124
127
|
}
|
|
125
128
|
}
|
|
@@ -189,6 +189,19 @@ function loadCalcViewInfo(context, entityType, cb) {
|
|
|
189
189
|
entityType.setCalculationViewDimensionData(rows, { cube: cubeName }, context.logger); // e.g. sap:label is fill with calculation view dimension data
|
|
190
190
|
entityType.resolveAggregates();
|
|
191
191
|
|
|
192
|
+
entityType.properties.forEach((property) => {
|
|
193
|
+
|
|
194
|
+
// suppressing annotation "sap:aggregation-role" depends on xsodata-file "setting"
|
|
195
|
+
if (entityType._settings.noDimensionAnnoOnTextProperty && entityType._settings.noDimensionAnnoOnTextProperty === 'true') {
|
|
196
|
+
if (property.DESC_NAME && property.DESC_NAME !== property.COLUMN_NAME) {
|
|
197
|
+
for (let i = 0; i < entityType.properties.length; i++) {
|
|
198
|
+
if (entityType.properties[i].COLUMN_NAME === property.DESC_NAME) {
|
|
199
|
+
entityType.properties[i].suppressAnnotationDimension = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
});
|
|
192
205
|
|
|
193
206
|
var parameters = entityType.getParameters();
|
|
194
207
|
if (!parameters) {
|