@cap-js-community/common 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -41,13 +41,14 @@ entity Books {
41
41
 
42
42
  ### Annotations
43
43
 
44
- Annotations can be used to enable replication cache for a service:
44
+ Annotations can be used to enable replication cache for a service on entity level:
45
45
 
46
46
  - `@cds.replicate: Boolean | Object`: Enable replication cache for entity
47
47
  - `@cds.replicate.ttl: Number`: Time-To-Live (TTL) of cache entry in milliseconds
48
48
  - `@cds.replicate.auto: Boolean`: Replication is managed automatically
49
49
  - `@cds.replicate.preload: Boolean`: Preload replication for entity
50
50
  - `@cds.replicate.group: String`: Replication group name
51
+ - `@cds.replicate.static: Boolean`: Statically replicate non-tenant aware
51
52
 
52
53
  Defaults are taken from CDS environment.
53
54
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js-community/common",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "CAP Node.js Community Common",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "engines": {
@@ -51,18 +51,18 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "@cap-js-community/common": "./",
54
- "@cap-js/cds-test": "^0.3.0",
55
- "@sap/cds": "^9.0.2",
54
+ "@cap-js/cds-test": "^0.4.0",
55
+ "@sap/cds": "^9.0.4",
56
56
  "@sap/cds-common-content": "^3.0.1",
57
- "@sap/cds-dk": "^9.0.4",
58
- "eslint": "9.28.0",
57
+ "@sap/cds-dk": "^9.0.5",
58
+ "eslint": "9.29.0",
59
59
  "eslint-config-prettier": "10.1.5",
60
- "eslint-plugin-jest": "28.12.0",
61
- "eslint-plugin-n": "^17.19.0",
62
- "jest": "29.7.0",
60
+ "eslint-plugin-jest": "29.0.1",
61
+ "eslint-plugin-n": "^17.20.0",
62
+ "jest": "30.0.3",
63
63
  "jest-html-reporters": "3.1.7",
64
64
  "jest-junit": "16.0.0",
65
- "prettier": "3.5.3",
65
+ "prettier": "3.6.1",
66
66
  "shelljs": "^0.10.0"
67
67
  },
68
68
  "cds": {
@@ -28,6 +28,7 @@ const Status = {
28
28
 
29
29
  const Annotations = {
30
30
  Replicate: "@cds.replicate",
31
+ ReplicateStatic: "@cds.replicate.static",
31
32
  ReplicateGroup: "@cds.replicate.group",
32
33
  ReplicateAuto: "@cds.replicate.auto",
33
34
  ReplicateTTL: "@cds.replicate.ttl",
@@ -61,7 +62,7 @@ class ReplicationCache {
61
62
  });
62
63
  cds.on("connect", (service) => {
63
64
  if (service.name === this.name) {
64
- const refs = ReplicationCache.replicationRefs(this.model, service);
65
+ const refs = ReplicationCache.replicationRefs(this.model, service, this.options.deploy);
65
66
  if (refs.length > 0) {
66
67
  this.setup(service, refs);
67
68
  this.log.info("using replication cache", {
@@ -82,12 +83,12 @@ class ReplicationCache {
82
83
  });
83
84
  }
84
85
 
85
- static replicationRefs(model, service) {
86
+ static replicationRefs(model, service, deploy) {
86
87
  const refs = Object.keys(model.definitions).filter((name) => {
87
88
  const definition = model.definitions[name];
88
89
  return (
89
90
  definition.kind === "entity" &&
90
- !definition.projection &&
91
+ (!(definition.query || definition.projection) || !deploy) &&
91
92
  (service.name === "db" || name.startsWith(`${service.name}.`)) &&
92
93
  Object.values(Annotations).find((annotation) => {
93
94
  return definition[annotation] !== undefined;
@@ -169,6 +170,14 @@ class ReplicationCache {
169
170
  if (!this.options.search && !this.search(req.query)) {
170
171
  return await next();
171
172
  }
173
+ let fromRefs = queryFromRefs(model, req.query);
174
+ if (this.options.deploy) {
175
+ fromRefs = baseRefs(model, fromRefs);
176
+ fromRefs = localizedRefs(model, req.query, fromRefs);
177
+ }
178
+ if (fromRefs.length === 0 || !this.relevant(fromRefs)) {
179
+ return await next();
180
+ }
172
181
  let refs = queryRefs(model, req.query);
173
182
  if (!this.options.deploy) {
174
183
  if (!this.localized(req.query, refs)) {
@@ -189,8 +198,12 @@ class ReplicationCache {
189
198
  this.stats.counts[ref] ??= 0;
190
199
  this.stats.counts[ref]++;
191
200
  }
201
+ let tenant = req.tenant;
202
+ if (staticRefs(model, refs)) {
203
+ tenant = undefined; // non-tenant cache
204
+ }
192
205
  const status = await this.load(
193
- req.tenant,
206
+ tenant,
194
207
  refs,
195
208
  {
196
209
  auto: this.options.auto,
@@ -203,7 +216,7 @@ class ReplicationCache {
203
216
  this.stats.used++;
204
217
  this.stats.ratio = Math.round(this.stats.used / this.stats.hits);
205
218
  this.log.debug("Replication cache was used");
206
- const db = this.cache.get(req.tenant).db;
219
+ const db = this.cache.get(tenant).db;
207
220
  if (this.options.measure) {
208
221
  return this.measure(
209
222
  async () => {
@@ -530,7 +543,7 @@ class ReplicationCache {
530
543
  let projections = true;
531
544
  for (const ref of refs) {
532
545
  const definition = model.definitions[ref];
533
- if (definition.query) {
546
+ if (definition.query || definition.projection) {
534
547
  this.stats.projections[ref] ??= 0;
535
548
  this.stats.projections[ref]++;
536
549
  this.log.debug("Replication cache not enabled for 'projections' without deploy feature", {
@@ -833,17 +846,24 @@ function localizedRefs(model, query, refs) {
833
846
  return unique(refs.concat(localizedRefs));
834
847
  }
835
848
 
849
+ function queryFromRefs(model, query) {
850
+ if (!query.SELECT) {
851
+ return [];
852
+ }
853
+ return unique(selectFromRefs(model, query));
854
+ }
855
+
836
856
  function queryRefs(model, query) {
837
857
  if (!query.SELECT) {
838
858
  return [];
839
859
  }
840
- return unique(fromRefs(model, query));
860
+ return unique(selectRefs(model, query));
841
861
  }
842
862
 
843
- function fromRefs(model, query) {
863
+ function selectFromRefs(model, query) {
844
864
  let refs = [];
845
865
  if (query.SELECT.from.SELECT) {
846
- refs = fromRefs(model, query.SELECT.from);
866
+ refs = selectFromRefs(model, query.SELECT.from);
847
867
  } else if (query.SELECT.from.ref) {
848
868
  refs = resolveRefs(model, query.SELECT.from.ref);
849
869
  } else if ((query.SELECT.from.join || query.SELECT.from.SET) && query.SELECT.from.args) {
@@ -852,20 +872,25 @@ function fromRefs(model, query) {
852
872
  return refs;
853
873
  }, []);
854
874
  }
875
+ return refs;
876
+ }
877
+
878
+ function selectRefs(model, query) {
879
+ let refs = selectFromRefs(model, query);
855
880
  if (query._target) {
856
881
  const target = model.definitions[query._target.name];
857
882
  if (query.SELECT.orderBy) {
858
- refs = refs.concat(expressionRefs(model, target, query.SELECT.orderBy));
883
+ refs = refs.concat(expressionRefs(model, target, query.SELECT.orderBy, query.SELECT.mixin));
859
884
  }
860
885
  if (query.SELECT.columns) {
861
- refs = refs.concat(expressionRefs(model, target, query.SELECT.columns));
862
- refs = refs.concat(expandRefs(model, target, query.SELECT.columns));
886
+ refs = refs.concat(expressionRefs(model, target, query.SELECT.columns, query.SELECT.mixin));
887
+ refs = refs.concat(expandRefs(model, target, query.SELECT.columns, query.SELECT.mixin));
863
888
  }
864
889
  if (query.SELECT.where) {
865
- refs = refs.concat(expressionRefs(model, target, query.SELECT.where));
890
+ refs = refs.concat(expressionRefs(model, target, query.SELECT.where, query.SELECT.mixin));
866
891
  }
867
892
  if (query.SELECT.having) {
868
- refs = refs.concat(expressionRefs(model, target, query.SELECT.having));
893
+ refs = refs.concat(expressionRefs(model, target, query.SELECT.having, query.SELECT.mixin));
869
894
  }
870
895
  }
871
896
  return refs;
@@ -892,14 +917,17 @@ function resolveRefs(model, refs) {
892
917
  return resolvedRefs;
893
918
  }
894
919
 
895
- function identifierRefs(model, definition, array) {
920
+ function identifierRefs(model, definition, expressions, mixin) {
896
921
  let refs = [];
897
- for (const entry of array) {
898
- if (Array.isArray(entry.ref)) {
922
+ for (const expression of expressions) {
923
+ if (Array.isArray(expression.ref)) {
899
924
  let current = definition;
900
- for (const ref of entry.ref) {
901
- if (current.elements[ref].type === "cds.Association" || current.elements[ref].type === "cds.Composition") {
902
- current = current.elements[ref]._target;
925
+ let currentMixin = mixin;
926
+ for (const ref of expression.ref) {
927
+ const element = current.elements[ref] || currentMixin?.[ref];
928
+ if (element.type === "cds.Association" || element.type === "cds.Composition") {
929
+ current = model.definitions[element.target];
930
+ currentMixin = {};
903
931
  refs.push(current.name);
904
932
  }
905
933
  }
@@ -908,27 +936,30 @@ function identifierRefs(model, definition, array) {
908
936
  return refs;
909
937
  }
910
938
 
911
- function expressionRefs(model, definition, array) {
912
- let refs = identifierRefs(model, definition, array);
913
- for (const entry of array) {
914
- if (entry.xpr) {
915
- refs = refs.concat(expressionRefs(model, definition, entry.xpr));
916
- } else if (entry.args) {
917
- refs = refs.concat(expressionRefs(model, definition, entry.args));
918
- } else if (entry.SELECT) {
919
- refs = refs.concat(fromRefs(model, entry));
939
+ function expressionRefs(model, definition, expressions, mixin) {
940
+ let refs = identifierRefs(model, definition, expressions, mixin);
941
+ for (const expression of expressions) {
942
+ if (expression.xpr) {
943
+ refs = refs.concat(expressionRefs(model, definition, expression.xpr, mixin));
944
+ } else if (expression.args) {
945
+ refs = refs.concat(expressionRefs(model, definition, expression.args, mixin));
946
+ } else if (expression.SELECT) {
947
+ refs = refs.concat(selectRefs(model, expression));
920
948
  }
921
949
  }
922
950
  return refs;
923
951
  }
924
952
 
925
- function expandRefs(model, definition, columns) {
953
+ function expandRefs(model, definition, columns, mixin) {
926
954
  let refs = [];
927
955
  for (const column of columns) {
928
956
  if (Array.isArray(column.ref) && column.expand) {
929
957
  let current = definition;
958
+ let currentMixin = mixin;
930
959
  for (const ref of column.ref) {
931
- current = current.elements[ref]._target;
960
+ const element = current.elements[ref] || currentMixin?.[ref];
961
+ current = model.definitions[element.target];
962
+ currentMixin = {};
932
963
  refs.push(current.name);
933
964
  }
934
965
  refs = refs.concat(expandRefs(model, current, column.expand));
@@ -937,6 +968,15 @@ function expandRefs(model, definition, columns) {
937
968
  return refs;
938
969
  }
939
970
 
971
+ function staticRefs(model, refs) {
972
+ for (const ref of refs) {
973
+ if (!model.definitions[ref][Annotations.ReplicateStatic]) {
974
+ return false;
975
+ }
976
+ }
977
+ return true;
978
+ }
979
+
940
980
  function cached(cache, field, init) {
941
981
  try {
942
982
  if (init && !cache.get(field)) {