@sap/xsodata 7.5.0 → 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 CHANGED
@@ -8,6 +8,18 @@ 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
+
11
23
  ## [7.5.0] - 2022-03-22
12
24
 
13
25
  * Support of node version 16
@@ -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>}
@@ -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.getKeyPropertiesNotSelectedForSelect = function (noTable) {
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];
@@ -134,9 +134,24 @@ JsonBuilder.prototype.createAbsUrl = function (dbSeg, row) {
134
134
  // although no input parameter given from client the application MUST have filled the input parameters as key (undefined via db is not possible)
135
135
  value = typeConverter.serializeDbValueToUriLiteral(row[keyProperty.COLUMN_NAME], keyProperty);
136
136
  } else {
137
- value = null;
137
+ if (dbSeg.hasAliasedKeyPropertiesOnCalcView()) {
138
+ let foundKeyFieldAlias = null;
139
+ for (let i = 0; i < dbSeg._aliasedKeyPropertiesOnCalcView.length; i++) {
140
+ if (dbSeg._aliasedKeyPropertiesOnCalcView[i].property === keyProperty.COLUMN_NAME &&
141
+ dbSeg._aliasedKeyPropertiesOnCalcView[i].table === dbSeg._Alias) {
142
+ foundKeyFieldAlias = dbSeg._aliasedKeyPropertiesOnCalcView[i].alias;
143
+ break; // there must not be more than one entry!
144
+ }
145
+ }
146
+ if (foundKeyFieldAlias !== null && row[foundKeyFieldAlias] !== undefined) {
147
+ value = typeConverter.serializeDbValueToUriLiteral(row[foundKeyFieldAlias], keyProperty);
148
+ } else {
149
+ value = null;
150
+ }
151
+ } else {
152
+ value = null;
153
+ }
138
154
  }
139
-
140
155
  } else {
141
156
  value = typeConverter.serializeDbValueToUriLiteral(row[keyProperty.COLUMN_NAME], keyProperty);
142
157
  }
@@ -167,13 +182,27 @@ JsonBuilder.prototype.serializeRow = function (context, dbSeg, row, converter) {
167
182
  ret.__metadata.etag = 'W/"' + row.__etag + '"';
168
183
  }
169
184
 
170
- var i;
171
-
172
- dbSeg.getSelectedPropsWithGenKey().forEach(function toConverter(property, index) {
173
- ret[property] = converter[index](row[property]);
174
- });
185
+ if (!dbSeg.hasAliasedKeyPropertiesOnCalcView()) {
186
+ dbSeg.getSelectedPropsWithGenKey().forEach(function toConverter(property, index) {
187
+ ret[property] = converter[index](row[property]);
188
+ });
189
+ } else {
190
+ let selectedProperties = dbSeg.getSelectedPropsWithGenKey();
191
+ for (let i = 0; i < selectedProperties.length; i++) {
192
+ if (!row[selectedProperties[i]]) {
193
+ let aliasedKeyProperty = dbSeg.getAliasedKeyPropertyOnCalcView(selectedProperties[i]);
194
+ if (aliasedKeyProperty !== null && aliasedKeyProperty.alias) {
195
+ ret[selectedProperties[i]] = converter[i](row[aliasedKeyProperty.alias]);
196
+ } else {
197
+ ret[selectedProperties[i]] = converter[i](row[selectedProperties[i]]); // keeps null
198
+ }
199
+ } else {
200
+ ret[selectedProperties[i]] = converter[i](row[selectedProperties[i]]);
201
+ }
202
+ }
203
+ }
175
204
 
176
- for (i = 0; i < dbSeg._SelectedNavigations.length; i++) {
205
+ for (let i = 0; i < dbSeg._SelectedNavigations.length; i++) {
177
206
  var sn = dbSeg._SelectedNavigations[i];
178
207
  var navDbSeg = dbSeg.getRelevantNavigationSegments()[sn];
179
208
  if (navDbSeg && navDbSeg.isExpand) {
@@ -191,8 +191,20 @@ function masterTableSimpleSelect(sqlContext, countFallback) {
191
191
  }
192
192
  } else {
193
193
  if (!isCount) {
194
- if (!(dbSeg.entityType.kind === EntityType.entityKind.calculationView && dbSeg.entityType._entityType.parameters && dbSeg.entityType._entityType.parameters.viaKey === true)) {
194
+ if (!(dbSeg.entityType.kind === EntityType.entityKind.calculationView &&
195
+ dbSeg.entityType._entityType.parameters &&
196
+ dbSeg.entityType._entityType.parameters.viaKey === true)) {
195
197
  select.addSortOrders(dbSeg.getKeyProperties0123ForOrderBy(true));
198
+ } else {
199
+ let orderByProperties = dbSeg.getKeyProperties0123ForOrderByCalcView(true);
200
+ for (let i= 0; i < select.select.length; i++) {
201
+ for (let j= 0; j < orderByProperties.length; j++) {
202
+ if (orderByProperties[j].getPropertyName() === select.select[i].property) {
203
+ select.select[i].alias = orderByProperties[j].expression.property;
204
+ }
205
+ }
206
+ }
207
+ select.addSortOrders(orderByProperties);
196
208
  }
197
209
  }
198
210
  }
@@ -384,16 +396,21 @@ function masterTableSimpleSelect(sqlContext, countFallback) {
384
396
  stm.addSelects(dbSeg.getAllSelectedPropertiesConsideringAggregates());
385
397
  } else {
386
398
  if (dbSeg.entityType.kind === EntityType.entityKind.calculationView && dbSeg.entityType._entityType.parameters && dbSeg.entityType._entityType.parameters.viaKey === true) {
387
- // stm.addSelect(dbSeg.getKeyProperties0123ForSelectAs0123());
388
399
  // Adding properties from getKeyProperties0123ForSelectAs0123 is not needed since
389
- // if (in future) a association via input parameter is used then the input parameter values must be added via ? into the sql command
390
- // that's because calcview don't return their input parameter in the
400
+ // if (in future) an association via input parameter is used then the input parameter values must be added via ? into the sql command
401
+ // that's because calcview don't return their input parameter in the result set
391
402
  let tmp = dbSeg.getPropertiesForSelectCollectInputParameters();
403
+
392
404
  // since the input parameters may not be returned from the calcview the parameters are merged after the select
393
- // from "calcview2" into the result.
405
+ // from "calcview2" into the result. // Q: What does that mean: "calcview2"?
394
406
  stm.byPassInputs = tmp.input;
395
407
  stm.addSelects(tmp.selects);
396
- stm.addSelects(dbSeg.getKeyPropertiesNotSelectedForSelect());
408
+
409
+ stm.addSelects(dbSeg.getKeyPropertiesNotSelectedForSelect(undefined, dbSeg.entityType.inputParameters));
410
+
411
+ // keep aliased key properties
412
+ let selectProperties = dbSeg.getKeyProperties0123ForSelectOnCalcViewAs0123();
413
+ dbSeg.setAliasedKeyPropertiesOnCalcView(selectProperties);
397
414
  } else {
398
415
  stm.addSelect(dbSeg.getKeyProperties0123ForSelectAs0123());
399
416
  stm.addSelects(dbSeg.getPropertiesForSelect());
@@ -620,7 +620,6 @@ function Select() {
620
620
  this.offset = null;
621
621
  this.fallbackStatement = null;
622
622
  this.addCalcViewHint = false;
623
-
624
623
  this.customData = {}; // restriction on the count of selected records
625
624
  }
626
625
 
@@ -661,6 +660,9 @@ Select.prototype.toSqlHana = function (context, parameter) {
661
660
  sql += this.select[i].toSqlHana(context, parameter);
662
661
  }
663
662
 
663
+ // note: $orderby properties for CV that are NOT in select list result in SQL error
664
+ // but: That was never supported by this lib!
665
+
664
666
  sql += '\nfrom ';
665
667
 
666
668
  sql += this.froms.map(function (from) {
@@ -998,7 +1000,6 @@ Select.prototype.addSelects = function (newSelects) {
998
1000
  }
999
1001
  }
1000
1002
  }
1001
-
1002
1003
  if (add) {
1003
1004
  this.select.push(newSelect);
1004
1005
  }
@@ -43,8 +43,9 @@ let XSODATA_FORCE_LOG_LONG_SQL = process.env.XSODATA_FORCE_LOG_LONG_SQL || 2;
43
43
  // 0-1: no SQL query parameters are logged
44
44
  // 2: SQL query parameters are logged
45
45
  let XSODATA_LOG_SQL_PARAMETERS = process.env.XSODATA_LOG_SQL_PARAMETERS || 2;
46
- let XSODATA_LOG_SQL_PARAM_VALUE_SIZE = process.env.XSODATA_LOG_SQL_PARAM_VALUE_SIZE || 30;
47
- let XSODATA_LOG_SQL_PARAM_ARRAY_SIZE = process.env.XSODATA_LOG_SQL_PARAM_ARRAY_SIZE || 30;
46
+ // XSODATA_LOG_SQL_PARAM_VALUE_SIZE: sets the length of the parameter value loggged out
47
+ let XSODATA_LOG_SQL_PARAM_VALUE_SIZE = process.env.XSODATA_LOG_SQL_PARAM_VALUE_SIZE || 80;
48
+ let XSODATA_LOG_SQL_PARAM_ARRAY_SIZE = process.env.XSODATA_LOG_SQL_PARAM_ARRAY_SIZE || 80;
48
49
 
49
50
  // Log long sql results
50
51
  // Requires XSODATA_LOG_SQL_COMMAND >= 2
@@ -1081,6 +1081,14 @@ function SortOrder(expression, ascending) {
1081
1081
  this.ascending = ascending;
1082
1082
  }
1083
1083
 
1084
+ SortOrder.prototype.setPropertyName = function(propertyName) {
1085
+ this.propertyName = propertyName;
1086
+ };
1087
+
1088
+ SortOrder.prototype.getPropertyName = function() {
1089
+ return this.propertyName;
1090
+ };
1091
+
1084
1092
  SortOrder.prototype.setAlias = function (alias) {
1085
1093
  this.expression.setAlias(alias);
1086
1094
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/xsodata",
3
- "version": "7.5.0",
3
+ "version": "7.5.5",
4
4
  "description": "Expose data from a HANA database as OData V2 service with help of .xsodata files.",
5
5
  "main": "index.js",
6
6
  "license": "SEE LICENSE IN developer-license-3.1.txt",
@@ -8,21 +8,17 @@
8
8
  "serve": "node test_apps/server",
9
9
  "serve-debug": "node --inspect-brk test_apps/server",
10
10
  "pretest": "npm -g ls --depth=0 && npm ls --depth=0",
11
- "test": "npm run unit-tests",
12
11
  "lint": "jshint .",
13
- "all-tests": "node ./node_modules/mocha/bin/_mocha --timeout 20000 test/**/test*.js test_apps/test_**/**/test*.js",
14
12
  "all-tests-jenkins": "node ./node_modules/mocha/bin/_mocha --timeout 20000 'test/**/test*.js' 'test_apps/test_**/**/test*.js'",
13
+ "all-tests-cv": "node ./node_modules/mocha/bin/_mocha --timeout 20000 'test_apps/test_cv/**/test*.js'",
15
14
  "scenario-tests": "node ./node_modules/mocha/bin/_mocha --timeout 20000 test_apps/test_**/**/test*.js",
16
15
  "unit-tests": "node ./node_modules/mocha/bin/_mocha test/**/test*.js",
17
- "cover": "istanbul cover -x test/**/* -x test_apps/**/* --report lcov --dir ./_coverage ./node_modules/mocha/bin/_mocha -- -R spec test/**/test*.js test_apps/**/test*.js",
18
- "cover-unit-tests": "istanbul cover -x test/**/* --report lcov --dir ./_coverage ./node_modules/mocha/bin/_mocha -- -R spec test/**/test*.js",
19
- "cover-scenario-tests": "istanbul cover -x test_apps/**/* --report lcov --dir ./_coverage ./node_modules/mocha/bin/_mocha -- -R spec test_apps/**/test*.js",
20
- "cover-jenkins-istanbul": "istanbul cover ./node_modules/mocha/bin/_mocha 'test/**/test*.js' 'test_apps/**/test*.js' -- --timeout 20000 && istanbul check-coverage ./_coverage/coverage.json",
16
+ "cover-unit-tests": "nyc --reporter=text --reporter=html --all --include lib/ npm run unit-tests",
17
+ "cover-scenario-tests": "nyc --reporter=text --reporter=html --all --include lib/ --check-coverage true --lines 80 --branches 69 --functions 87 --statements 80 npm run scenario-tests",
21
18
  "cover-jenkins": "nyc --reporter=text --reporter=html --all --include lib/ --check-coverage true --lines 80 --branches 69 --functions 87 --statements 80 npm run all-tests-jenkins",
22
- "report": "node ./node_modules/mocha/bin/_mocha test/**/test*.js test_apps/test_**/**/test*.js --reporter mocha-simple-html-reporter --reporter-options output=_gen/report/report_all.html",
23
- "report-unit-tests": "node ./node_modules/mocha/bin/_mocha test/**/test*.js --reporter mocha-simple-html-reporter --reporter-options output=_gen/report/report_unit.html",
24
- "report-scenario-tests": "node ./node_modules/mocha/bin/_mocha test_apps/test_**/**/test*.js --reporter mocha-simple-html-reporter --reporter-options output=_gen/report/report_scenario.html",
25
- "env": "env",
19
+ "report": "node ./node_modules/mocha/bin/_mocha test/**/test*.js test_apps/test_**/**/test*.js --reporter mocha-simple-html-reporter --reporter-options output=report_all.html",
20
+ "report-unit-tests": "node ./node_modules/mocha/bin/_mocha test/**/test*.js --reporter mocha-simple-html-reporter --reporter-options output=report_unit.html",
21
+ "report-scenario-tests": "node ./node_modules/mocha/bin/_mocha test_apps/test_**/**/test*.js --reporter mocha-simple-html-reporter --reporter-options output=report_scenario.html",
26
22
  "gen": "npm run genfilter && npm run genorderby && npm run gensegment && npm run genxso && npm run genxml && npm run genDateTimeParser",
27
23
  "genfilter": "jison lib/grammars/filter.jison -o lib/parsers/jison_filter_parser.js",
28
24
  "genorderby": "jison lib/grammars/orderby.jison -o lib/parsers/jison_orderby_parser.js",
@@ -68,10 +64,9 @@
68
64
  "chai": "4.2.0",
69
65
  "expect": "1.20.2",
70
66
  "filter-node-package": "=2.2.0",
71
- "istanbul": "0.4.5",
72
67
  "jison": "0.4.18",
73
- "jshint": "2.10.2",
74
- "mocha": "8.3.2",
68
+ "jshint": "2.13.4",
69
+ "mocha": "9.2.2",
75
70
  "mocha-simple-html-reporter": "1.1.0",
76
71
  "node-mocks-http": "=1.7.6",
77
72
  "nyc": "^15.1.0",