cassproject 5.0.15 → 5.0.17

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/package.json CHANGED
@@ -1,15 +1,24 @@
1
1
  {
2
2
  "name": "cassproject",
3
- "version": "5.0.15",
3
+ "version": "5.0.17",
4
4
  "description": "Competency and Skills Service",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "concurrently --maxProcesses=1 --kill-others-on-fail \"npm run test16HttpsFips\" \"npm run test16Https11Fips\" \"npm run test16Https\" \"npm run test16Https11\" \"npm run test16Fips\" \"npm run test16\" \"npm run test15HttpsFips\" \"npm run test15Https11Fips\" \"npm run test15Https\" \"npm run test15Https11\" \"npm run test15Fips\" \"npm run test15\"",
7
+ "test": "concurrently --maxProcesses=1 --kill-others-on-fail \"npm run test17HttpsFips\" \"npm run test17Https11Fips\" \"npm run test17Https\" \"npm run test17Https11\" \"npm run test17Fips\" \"npm run test17\" \"npm run test16HttpsFips\" \"npm run test16Https11Fips\" \"npm run test16Https\" \"npm run test16Https11\" \"npm run test16Fips\" \"npm run test16\" \"npm run test15HttpsFips\" \"npm run test15Https11Fips\" \"npm run test15Https\" \"npm run test15Https11\" \"npm run test15Fips\" \"npm run test15\"",
8
8
  "testActionCypress": "npm run testkill && export CASS_LOOPBACK=http://cass-test15/api/|| set CASS_LOOPBACK=http://cass-test15/api/&& npm run testkill15 && docker run -d --network cass-net -e CASS_LOOPBACK --name cass-test15 -p80:80 cassproject/cass:1.5.75 && wait-on http://localhost/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\"",
9
9
  "testCustom": "npm run test14 && npm run test13 && npm run test12",
10
10
  "multitest": "concurrently --kill-others-on-fail \"npm run test15\" \"npm run test14\" \"npm run test13\" \"npm run test12\"",
11
11
  "testCassTest": "npm run testkill && docker run -d --network cass-net --name cass-test -p80:80 -e CASS_LOOPBACK cass-test && wait-on http://localhost/api/ping && npm run testNode18 && npm run testNode18Fips && npm run testCypressEdge && npm run testCypress && npm run testkill",
12
12
  "testDevHttps": "npm run testkill && docker run -d --network cass-net --name cass-test -p443:80 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:dev && wait-on https://localhost/api/ping && npm run testNode18HttpsFips && npm run testNode18Https && npm run testCypressEdgeHttps && npm run testCypressHttps",
13
+ "test17HttpsFips": "export CASS_LOOPBACK=https://cass-testsf17/api/|| set CASS_LOOPBACK=https://cass-testsf17/api/&& npm run testkillsf17 && docker run -d --network cass-net --name cass-testsf17 -p454:443 -e CASS_LOOPBACK -e HTTPS=true --pull always cassproject/cass:1.7 && wait-on https://localhost:454/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode24HttpsForceFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode22HttpsForceFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode20HttpsForceFips\" \"npm run testNode18Https\" && npm run testkillsf17",
14
+ "test17Https11Fips": "export CASS_LOOPBACK=https://cass-testsf1117/api/|| set CASS_LOOPBACK=https://cass-testsf1117/api/&& npm run testkillsf1117 && docker run -d --network cass-net --name cass-testsf1117 -p453:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.7 && wait-on https://localhost:453/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode24HttpsForceFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode22HttpsForceFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode20HttpsForceFips\" \"npm run testNode18Https\" && npm run testkillsf1117",
15
+ "test17Https": "export CASS_LOOPBACK=https://cass-tests17/api/|| set CASS_LOOPBACK=https://cass-tests17/api/&& npm run testkills17 && docker run -d --network cass-net --name cass-tests17 -p452:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.7 && wait-on https://localhost:452/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode18Https\" && npm run testkills17",
16
+ "test17HttpsNoFips": "export CASS_LOOPBACK=https://cass-tests17/api/|| set CASS_LOOPBACK=https://cass-tests17/api/&& npm run testkills17 && docker run -d --network cass-net --name cass-tests17 -p452:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.7 && wait-on https://localhost:452/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode22Https\" \"npm run testNode20Https\" \"npm run testNode18Https\" && npm run testkills17",
17
+ "test17Https11": "export CASS_LOOPBACK=https://cass-tests1117/api/|| set CASS_LOOPBACK=https://cass-tests1117/api/&& npm run testkills1117 && docker run -d --network cass-net --name cass-tests1117 -p455:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.7 && wait-on https://localhost:455/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode18Https\" && npm run testkills1117",
18
+ "test17Https11NoFips": "export CASS_LOOPBACK=https://cass-tests1117/api/|| set CASS_LOOPBACK=https://cass-tests1117/api/&& npm run testkills1117 && docker run -d --network cass-net --name cass-tests1117 -p455:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.7 && wait-on https://localhost:455/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode22Https\" \"npm run testNode20Https\" \"npm run testNode18Https\" && npm run testkills1117",
19
+ "test17Fips": "export CASS_LOOPBACK=http://cass-testf17/api/|| set CASS_LOOPBACK=http://cass-testf17/api/&& npm run testkillf17 && docker run -d --network cass-net -e CASS_LOOPBACK --name cass-testf17 -p85:80 cassproject/cass:1.7 && wait-on http://localhost:85/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode24\" \"npm run testNode24Fips\" \"npm run testNode24ForceFips\" \"npm run testNode22\" \"npm run testNode22Fips\" \"npm run testNode22ForceFips\" \"npm run testNode20\" \"npm run testNode20Fips\" \"npm run testNode20ForceFips\" \"npm run testNode18\" && npm run testkillf17",
20
+ "test17": "export CASS_LOOPBACK=http://cass-test17/api/|| set CASS_LOOPBACK=http://cass-test17/api/&& npm run testkill17 && docker run -d --network cass-net -e CASS_LOOPBACK --name cass-test17 -p84:80 cassproject/cass:1.7 && wait-on http://localhost:84/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode24\" \"npm run testNode24Fips\" \"npm run testNode22\" \"npm run testNode22Fips\" \"npm run testNode20\" \"npm run testNode20Fips\" \"npm run testNode18\" && npm run testkill17",
21
+ "test17NoFips": "export CASS_LOOPBACK=http://cass-test17/api/|| set CASS_LOOPBACK=http://cass-test17/api/&& npm run testkill17 && docker run -d --network cass-net -e CASS_LOOPBACK --name cass-test17 -p84:80 cassproject/cass:1.7 && wait-on http://localhost:84/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode24\" \"npm run testNode22\" \"npm run testNode20\" \"npm run testNode18\" && npm run testkill17",
13
22
  "test16HttpsFips": "export CASS_LOOPBACK=https://cass-testsf16/api/|| set CASS_LOOPBACK=https://cass-testsf16/api/&& npm run testkillsf16 && docker run -d --network cass-net --name cass-testsf16 -p450:443 -e CASS_LOOPBACK -e HTTPS=true --pull always cassproject/cass:1.6 && wait-on https://localhost:450/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode24HttpsForceFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode22HttpsForceFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode20HttpsForceFips\" \"npm run testNode18Https\" && npm run testkillsf16",
14
23
  "test16Https11Fips": "export CASS_LOOPBACK=https://cass-testsf1116/api/|| set CASS_LOOPBACK=https://cass-testsf1116/api/&& npm run testkillsf1116 && docker run -d --network cass-net --name cass-testsf1116 -p449:443 -e CASS_LOOPBACK -e HTTPS=true -e HTTP2=false cassproject/cass:1.6 && wait-on https://localhost:449/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode24HttpsForceFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode22HttpsForceFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode20HttpsForceFips\" \"npm run testNode18Https\" && npm run testkillsf1116",
15
24
  "test16Https": "export CASS_LOOPBACK=https://cass-tests16/api/|| set CASS_LOOPBACK=https://cass-tests16/api/&& npm run testkills16 && docker run -d --network cass-net --name cass-tests16 -p448:443 -e CASS_LOOPBACK -e HTTPS=true cassproject/cass:1.6 && wait-on https://localhost:448/api/ping && concurrently --kill-others-on-fail \"npm run testCypressHttps\" \"npm run testNode24Https\" \"npm run testNode24HttpsFips\" \"npm run testNode22Https\" \"npm run testNode22HttpsFips\" \"npm run testNode20Https\" \"npm run testNode20HttpsFips\" \"npm run testNode18Https\" && npm run testkills16",
@@ -31,6 +40,12 @@
31
40
  "test14": "npm run testkill14 && docker run -d --network cass-net -e CASS_LOOPBACK --name cass-test14 -p80:80 cassproject/cass:1.4.4 && wait-on http://localhost/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode16\" && npm run testkill14",
32
41
  "test13": "npm run testkill13 && docker run -d --network cass-net -e CASS_LOOPBACK --name cass-test13 -p80:80 cassproject/cass:1.3.18 && wait-on http://localhost/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode16\" && npm run testkill13",
33
42
  "test12": "npm run testkill12 && docker run -d --network cass-net -e CASS_LOOPBACK --name cass-test12 -p80:80 cassproject/cass:1.2.44 && wait-on http://localhost/api/ping && concurrently --kill-others-on-fail \"npm run testCypress\" \"npm run testNode16\" && npm run testkill12",
43
+ "testkillsf17": "docker kill cass-testsf17 | exit 0 && docker rm cass-testsf17 | exit 0 && docker network create cass-net | exit 0",
44
+ "testkillsf1117": "(docker kill cass-testsf1117 | exit 0) && (docker rm cass-testsf1117 | exit 0) && docker network create cass-net | exit 0",
45
+ "testkills17": "docker kill cass-tests17 | exit 0 && docker rm cass-tests17 | exit 0 && docker network create cass-net | exit 0",
46
+ "testkills1117": "docker kill cass-tests1117 | exit 0 && docker rm cass-tests1117 | exit 0 && docker network create cass-net | exit 0",
47
+ "testkillf17": "docker kill cass-testf17 | exit 0 && docker rm cass-testf17 | exit 0 && docker network create cass-net | exit 0",
48
+ "testkill17": "docker kill cass-test17 | exit 0 && docker rm cass-test17 | exit 0 && docker network create cass-net | exit 0",
34
49
  "testkillsf16": "docker kill cass-testsf16 | exit 0 && docker rm cass-testsf16 | exit 0 && docker network create cass-net | exit 0",
35
50
  "testkillsf1116": "(docker kill cass-testsf1116 | exit 0) && (docker rm cass-testsf1116 | exit 0) && docker network create cass-net | exit 0",
36
51
  "testkills16": "docker kill cass-tests16 | exit 0 && docker rm cass-tests16 | exit 0 && docker network create cass-net | exit 0",
@@ -143,7 +158,7 @@
143
158
  "base64-arraybuffer": "^1.0.2",
144
159
  "jsonld": "^9.0.0",
145
160
  "node-forge": "^1.4.0",
146
- "papaparse": "^5.5.3",
161
+ "papaparse": "^5.5.4",
147
162
  "promise-worker": "^2.0.1",
148
163
  "web-worker": "1.3.0"
149
164
  },
@@ -169,20 +184,20 @@
169
184
  },
170
185
  "homepage": "https://github.com/cassproject/cass-npm#readme",
171
186
  "devDependencies": {
172
- "@cypress/webpack-preprocessor": "^7.1.0",
187
+ "@cypress/webpack-preprocessor": "^7.1.1",
173
188
  "chai": "^4.5.0",
174
- "concurrently": "^10.0.0",
175
- "cypress": "^15.16.0",
189
+ "concurrently": "^10.0.4",
190
+ "cypress": "15.19.0",
176
191
  "cypress-fail-fast": "^8.1.0",
177
- "eslint": "^10.4.1",
192
+ "eslint": "^10.8.0",
178
193
  "fake-indexeddb": "^6.2.5",
179
194
  "mocha": "^11.7.6",
180
195
  "node-polyfill-webpack-plugin": "^4.1.0",
181
196
  "nodemon": "^3.1.14",
182
197
  "nyc": "^18.0.0",
183
198
  "sinon": "^21.1.2",
184
- "wait-on": "^9.0.10",
185
- "webpack": "^5.107.2"
199
+ "wait-on": "^9.1.0",
200
+ "webpack": "^5.109.1"
186
201
  },
187
202
  "packageManager": "npm@11.3.0+sha512.96eb611483f49c55f7fa74df61b588de9e213f80a256728e6798ddc67176c7b07e4a1cfc7de8922422cbce02543714367037536955221fa451b0c4fefaf20c66"
188
203
  }
@@ -1,4 +1,3 @@
1
- const { v4: uuidv4 } = require('uuid');
2
1
  let forge = require("node-forge");
3
2
  /**
4
3
  * @author Fritz
@@ -71,9 +70,19 @@ module.exports = class EcCrypto {
71
70
  return new TextEncoder("utf-8").encode(str).buffer;
72
71
  }
73
72
  static generateUUID()
74
- {
75
- let uuid = uuidv4();
76
- uuid = uuid.substr(0,9)+'c455'+uuid.substr(13);
77
- return uuid;
73
+ {
74
+ var d = new Date().getTime(); //Timestamp
75
+ var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
76
+ return 'xxxxxxxx-c455-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
77
+ var r = Math.random() * 16;//random number between 0 and 16
78
+ if (d > 0) {//Use timestamp until depleted
79
+ r = (d + r) % 16 | 0;
80
+ d = Math.floor(d / 16);
81
+ } else {//Use microseconds since page-load if supported
82
+ r = (d2 + r) % 16 | 0;
83
+ d2 = Math.floor(d2 / 16);
84
+ }
85
+ return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
86
+ });
78
87
  }
79
88
  };
package/src/index/env.js CHANGED
@@ -4,16 +4,19 @@ if (typeof window !== "undefined" && window) {
4
4
  }
5
5
 
6
6
  global.generateUUID = function () {
7
- let d = new Date().getTime();
8
- if (typeof window !== "undefined" && window && window.performance && typeof window.performance.now === "function") {
9
- d += performance.now(); // use high-precision timer if available
10
- }
11
- let uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
12
- let r = (d + Math.random() * 16) % 16 | 0;
13
- d = Math.floor(d / 16);
14
- return (c == 'x' ? r : r & 0x3 | 0x8).toString(16);
7
+ var d = new Date().getTime(); //Timestamp
8
+ var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
9
+ return 'xxxxxxxx-c455-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
10
+ var r = Math.random() * 16;//random number between 0 and 16
11
+ if (d > 0) {//Use timestamp until depleted
12
+ r = (d + r) % 16 | 0;
13
+ d = Math.floor(d / 16);
14
+ } else {//Use microseconds since page-load if supported
15
+ r = (d2 + r) % 16 | 0;
16
+ d2 = Math.floor(d2 / 16);
17
+ }
18
+ return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
15
19
  });
16
- return uuid;
17
20
  };
18
21
 
19
22
  try {
@@ -122,13 +122,12 @@ module.exports = class EcLevel extends Level {
122
122
  repo, eim
123
123
  ) {
124
124
  let a = new EcAlignment();
125
- a.source = this.id;
126
- a.target = targetLevel.id;
125
+ a.source = this.shortId();
126
+ a.target = targetLevel.shortId();
127
127
  a.relationType = alignmentType;
128
128
  a.addOwner(identity.toPk());
129
129
  if (repo?.selectedServer != null)
130
130
  a.generateId(repo.selectedServer);
131
- await a.signWith(identity);
132
131
  await a.save(success, failure, repo, eim);
133
132
  return a;
134
133
  }
@@ -134,6 +134,23 @@ module.exports = class EcRepository {
134
134
  return Reflect.deleteProperty(...arguments);
135
135
  }
136
136
  });
137
+ /**
138
+ * Empties the in-memory cache and, if L2 caching is enabled, the
139
+ * IndexedDB backing store. Do not reassign cacheBacking to clear the
140
+ * cache -- the cache proxy permanently wraps the original object.
141
+ *
142
+ * @memberOf EcRepository
143
+ * @method clearCache
144
+ * @static
145
+ */
146
+ static clearCache = () => {
147
+ for (let prop of Object.keys(EcRepository.cacheBacking))
148
+ delete EcRepository.cacheBacking[prop];
149
+ if (EcRepository.cachingL2 == true && EcRepository.cacheDB != null) {
150
+ const transaction = EcRepository.cacheDB.transaction(EcRepository.LONGIDS, "readwrite");
151
+ transaction.objectStore(EcRepository.LONGIDS).clear();
152
+ }
153
+ };
137
154
  static fetching = {};
138
155
  static repos = [];
139
156
  static defaultPlugins = [];
@@ -146,7 +163,9 @@ module.exports = class EcRepository {
146
163
  postMaxSize = null;
147
164
  init(selectedServer, success, failure, loginObjectCallback) {
148
165
  this.selectedServer = selectedServer;
149
- return this.negotiateTimeOffset(success, failure, loginObjectCallback);
166
+ return this.negotiateTimeOffset(success, failure, loginObjectCallback).then(()=>{
167
+ return this.fetchServerAdminKeys()
168
+ });
150
169
  }
151
170
  negotiateTimeOffset = function (success, failure, loginObjectCallback) {
152
171
  let oldTimeout = EcRemote.timeout;
@@ -781,16 +800,7 @@ module.exports = class EcRepository {
781
800
  } else {
782
801
  offset = repo.timeOffset;
783
802
  }
784
- if (data.owner != null && data.owner.length > 0) {
785
- p = eim.signatureSheetFor(
786
- data.owner,
787
- 300000 + offset,
788
- data.id,
789
- null, null, repo != null ? repo.signatureSheetHashAlgorithm : null
790
- );
791
- } else {
792
- p = eim.signatureSheet(300000 + offset, data.id, null, null, repo != null ? repo.signatureSheetHashAlgorithm : null);
793
- }
803
+ p = eim.signatureSheet(300000 + offset, data.id, null, null, repo != null ? repo.signatureSheetHashAlgorithm : null);
794
804
  p = p.then((signatureSheet) => {
795
805
  let fd = new FormData();
796
806
  fd.append("data", data.toJson());
@@ -895,23 +905,18 @@ module.exports = class EcRepository {
895
905
  let targetUrl;
896
906
  targetUrl = data.shortId();
897
907
  let offset = this.setOffset(data.id);
898
- if (data.owner != null && data.owner.length > 0) {
899
- return eim.signatureSheetFor(
900
- data.owner,
901
- 300000 + offset,
902
- data.id,
903
- null, null, repo != null ? repo.signatureSheetHashAlgorithm : null
904
- ).then((signatureSheet) => {
905
- return EcRemote._delete(
906
- targetUrl,
907
- signatureSheet,
908
- success,
909
- failure
910
- );
911
- });
912
- } else {
913
- return EcRemote._delete(targetUrl, [], success, failure);
914
- }
908
+ return eim.signatureSheet(
909
+ 300000 + offset,
910
+ data.id,
911
+ null, null, repo != null ? repo.signatureSheetHashAlgorithm : null
912
+ ).then((signatureSheet) => {
913
+ return EcRemote._delete(
914
+ targetUrl,
915
+ signatureSheet,
916
+ success,
917
+ failure
918
+ );
919
+ });
915
920
  };
916
921
  /**
917
922
  * Attempts to delete a piece of data.
@@ -965,23 +970,18 @@ module.exports = class EcRepository {
965
970
  EcCrypto.md5(data.shortId())
966
971
  );
967
972
  let offset = EcRepository.setOffset(data.id);
968
- if (data.owner != null && data.owner.length > 0) {
969
- return eim.signatureSheetFor(
970
- data.owner,
971
- 300000 + offset,
972
- data.id,
973
- null, null, this != null ? this.signatureSheetHashAlgorithm : null
974
- ).then((signatureSheet) => {
975
- return EcRemote._delete(
976
- targetUrl,
977
- signatureSheet,
978
- success,
979
- failure
980
- );
981
- });
982
- } else {
983
- return EcRemote._delete(targetUrl, [], success, failure);
984
- }
973
+ return eim.signatureSheet(
974
+ 300000 + offset,
975
+ data.id,
976
+ null, null, this != null ? this.signatureSheetHashAlgorithm : null
977
+ ).then((signatureSheet) => {
978
+ return EcRemote._delete(
979
+ targetUrl,
980
+ signatureSheet,
981
+ success,
982
+ failure
983
+ );
984
+ });
985
985
  };
986
986
  /**
987
987
  * Attempts to save many pieces of data. Does some checks before saving to
@@ -1001,7 +1001,6 @@ module.exports = class EcRepository {
1001
1001
  multiput = function (data, success, failure, eim) {
1002
1002
  if (eim === undefined || eim == null)
1003
1003
  eim = EcIdentityManager.default;
1004
- let allOwners = [];
1005
1004
  for (let d of data) {
1006
1005
  if (d.invalid())
1007
1006
  throw new Error("Cannot save data. It is missing a vital component.");
@@ -1034,9 +1033,6 @@ module.exports = class EcRepository {
1034
1033
  )
1035
1034
  ];
1036
1035
  }
1037
- if (d.owner != null)
1038
- for (let j = 0; j < d.owner.length; j++)
1039
- EcArray.setAdd(allOwners, d.owner[j]);
1040
1036
  }
1041
1037
  let encryptionAndSigningPromises = data.map((d) => {
1042
1038
  return cassReturnAsPromise(d)
@@ -1064,19 +1060,10 @@ module.exports = class EcRepository {
1064
1060
  return Promise.all(encryptionAndSigningPromises)
1065
1061
  .then((readyToSendData) => {
1066
1062
  preparedData = readyToSendData;
1067
- if (allOwners != null && allOwners.length > 0) {
1068
- return eim.signatureSheetFor(
1069
- allOwners,
1070
- 300000 + this.timeOffset,
1071
- this.selectedServer,
1072
- null, null, this.signatureSheetHashAlgorithm
1073
- );
1074
- } else {
1075
- return eim.signatureSheet(
1076
- 300000 + this.timeOffset,
1077
- this.selectedServer, null, null, this.signatureSheetHashAlgorithm
1078
- );
1079
- }
1063
+ return eim.signatureSheet(
1064
+ 300000 + this.timeOffset,
1065
+ this.selectedServer, null, null, this.signatureSheetHashAlgorithm
1066
+ );
1080
1067
  })
1081
1068
  .then((signatureSheet) => {
1082
1069
  let fd = new FormData();
@@ -1950,7 +1937,7 @@ module.exports = class EcRepository {
1950
1937
  service = "/sky/admin";
1951
1938
  }
1952
1939
  let me = this;
1953
- EcRemote.getExpectingObject(
1940
+ return EcRemote.getExpectingObject(
1954
1941
  this.selectedServer,
1955
1942
  service,
1956
1943
  function (p1) {
@@ -1959,10 +1946,16 @@ module.exports = class EcRepository {
1959
1946
  for (let i = 0; i < ary.length; i++) {
1960
1947
  me.adminKeys.push(ary[i]);
1961
1948
  }
1962
- success(ary);
1949
+ if (success != null)
1950
+ success(ary);
1951
+ else
1952
+ return ary;
1963
1953
  },
1964
1954
  function (p1) {
1965
- failure("");
1955
+ if (failure != null)
1956
+ failure(p1);
1957
+ else
1958
+ throw new Error(p1);
1966
1959
  }
1967
1960
  );
1968
1961
  };
@@ -1981,9 +1974,7 @@ module.exports = class EcRepository {
1981
1974
  } else
1982
1975
  return p1;
1983
1976
  }
1984
- p1 = await EcEncryptedValue.fromEncryptedValue(
1985
- p1, null, null, eim
1986
- );
1977
+ p1 = await EcEncryptedValue.fromEncryptedValue(p1, null, null, eim);
1987
1978
  if (p1.isAny(result.getTypes())) {
1988
1979
  result.copyFrom(p1, eim);
1989
1980
  if (this.caching) {
@@ -0,0 +1,179 @@
1
+ require('fake-indexeddb/auto');
2
+
3
+ const schema = {
4
+ "Thing": require('../org/schema/Thing.js')
5
+ };
6
+
7
+ let chai = require("chai");
8
+ const EcRepository = require('../org/cassproject/ebac/repository/EcRepository.js');
9
+ const EcCrypto = require('../com/eduworks/ec/crypto/EcCrypto.js');
10
+ const EcRemoteLinkedData = require('../org/cassproject/schema/general/EcRemoteLinkedData.js');
11
+
12
+ let assert = chai.assert;
13
+
14
+ const TEST_DB = "EcRepositoryCacheTest";
15
+
16
+ // Ids that contain /api/data/ and end in /<digits> are "versioned" and eligible
17
+ // for L2 caching; anything else is L1-only.
18
+ let versionedId = () => "http://localhost/api/data/schema.org.Thing/" + EcCrypto.generateUUID() + "/1690000000000";
19
+ let unversionedId = () => "http://localhost/api/data/schema.org.Thing/" + EcCrypto.generateUUID();
20
+
21
+ let makeThing = (id) => {
22
+ let thing = new schema.Thing();
23
+ thing.id = id;
24
+ thing.setName("Some Thing " + EcCrypto.generateUUID());
25
+ return thing;
26
+ };
27
+
28
+ let openTestDb = () => new Promise((resolve, reject) => {
29
+ const request = indexedDB.open(TEST_DB, 3);
30
+ request.onerror = reject;
31
+ request.onupgradeneeded = (event) => {
32
+ event.target.result.createObjectStore(EcRepository.LONGIDS, { keyPath: "id" });
33
+ };
34
+ request.onsuccess = (event) => resolve(event.target.result);
35
+ });
36
+
37
+ // Reads straight from IndexedDB, bypassing L1, to observe what the proxy wrote.
38
+ let l2Get = (id) => new Promise((resolve, reject) => {
39
+ const transaction = EcRepository.cacheDB.transaction(EcRepository.LONGIDS, "readonly");
40
+ const request = transaction.objectStore(EcRepository.LONGIDS).get(id);
41
+ request.onerror = () => reject(request.error);
42
+ request.onsuccess = () => resolve(request.result == null ? null : request.result);
43
+ });
44
+
45
+ let l2Count = () => new Promise((resolve, reject) => {
46
+ const transaction = EcRepository.cacheDB.transaction(EcRepository.LONGIDS, "readonly");
47
+ const request = transaction.objectStore(EcRepository.LONGIDS).count();
48
+ request.onerror = () => reject(request.error);
49
+ request.onsuccess = () => resolve(request.result);
50
+ });
51
+
52
+ describe("EcRepository (Cache Management)", () => {
53
+ before(async () => {
54
+ EcRepository.caching = true;
55
+ EcRepository.cachingL2 = false;
56
+ EcRepository.clearCache();
57
+ EcRepository.cacheDB = await openTestDb();
58
+ });
59
+ after(() => {
60
+ EcRepository.cachingL2 = true;
61
+ EcRepository.clearCache();
62
+ EcRepository.caching = false;
63
+ EcRepository.cachingL2 = false;
64
+ EcRepository.cacheDB.close();
65
+ EcRepository.cacheDB = null;
66
+ indexedDB.deleteDatabase(TEST_DB);
67
+ });
68
+
69
+ describe("L1", () => {
70
+ before(() => {
71
+ EcRepository.cachingL2 = false;
72
+ });
73
+ it('cache proxy reads and writes the cacheBacking object', async () => {
74
+ let id = unversionedId();
75
+ let thing = makeThing(id);
76
+ EcRepository.cache[id] = thing;
77
+ assert.strictEqual(EcRepository.cacheBacking[id], thing, "Write through proxy did not land in cacheBacking.");
78
+ EcRepository.cacheBacking[id + "/direct"] = thing;
79
+ assert.strictEqual(EcRepository.cache[id + "/direct"], thing, "Write to cacheBacking not visible through proxy.");
80
+ assert.strictEqual(await EcRepository.cacheGet(id), thing);
81
+ delete EcRepository.cache[id + "/direct"];
82
+ });
83
+ it('delete through the proxy removes the entry', async () => {
84
+ let id = unversionedId();
85
+ EcRepository.cache[id] = makeThing(id);
86
+ delete EcRepository.cache[id];
87
+ assert.isUndefined(EcRepository.cacheBacking[id]);
88
+ assert.isUndefined(await EcRepository.cacheGet(id));
89
+ });
90
+ it('clearCache empties L1', async () => {
91
+ let ids = [unversionedId(), unversionedId(), versionedId()];
92
+ for (let id of ids)
93
+ EcRepository.cache[id] = makeThing(id);
94
+ EcRepository.clearCache();
95
+ assert.equal(Object.keys(EcRepository.cacheBacking).length, 0);
96
+ for (let id of ids)
97
+ assert.isUndefined(await EcRepository.cacheGet(id));
98
+ });
99
+ });
100
+
101
+ describe("L2", () => {
102
+ before(() => {
103
+ EcRepository.cachingL2 = true;
104
+ });
105
+ it('versioned writes go to both L1 and IndexedDB', async () => {
106
+ let id = versionedId();
107
+ EcRepository.cache[id] = makeThing(id);
108
+ assert.isDefined(EcRepository.cacheBacking[id], "Entry missing from L1.");
109
+ let stored = await l2Get(id);
110
+ assert.isNotNull(stored, "Entry missing from IndexedDB.");
111
+ assert.equal(stored.id, id);
112
+ });
113
+ it('unversioned writes stay in L1 only', async () => {
114
+ let id = unversionedId();
115
+ EcRepository.cache[id] = makeThing(id);
116
+ assert.isDefined(EcRepository.cacheBacking[id], "Entry missing from L1.");
117
+ assert.isNull(await l2Get(id), "Unversioned entry should not be in IndexedDB.");
118
+ });
119
+ it('cacheGet falls back to IndexedDB on L1 miss', async () => {
120
+ let id = versionedId();
121
+ let thing = makeThing(id);
122
+ EcRepository.cache[id] = thing;
123
+ // Evict from L1 only; a proxy delete would also remove the L2 copy.
124
+ delete EcRepository.cacheBacking[id];
125
+ let got = await EcRepository.cacheGet(id);
126
+ assert.instanceOf(got, EcRemoteLinkedData);
127
+ assert.equal(got.id, id);
128
+ assert.equal(got.getName(), thing.getName());
129
+ });
130
+ it('delete through the proxy removes the entry from both layers', async () => {
131
+ let id = versionedId();
132
+ EcRepository.cache[id] = makeThing(id);
133
+ delete EcRepository.cache[id];
134
+ assert.isUndefined(EcRepository.cacheBacking[id], "Entry still in L1.");
135
+ assert.isNull(await l2Get(id), "Entry still in IndexedDB.");
136
+ assert.isUndefined(await EcRepository.cacheGet(id));
137
+ });
138
+ it('setting an entry to null removes it from IndexedDB', async () => {
139
+ let id = versionedId();
140
+ EcRepository.cache[id] = makeThing(id);
141
+ assert.isNotNull(await l2Get(id));
142
+ EcRepository.cache[id] = null;
143
+ assert.isNull(await l2Get(id), "Entry still in IndexedDB.");
144
+ assert.isNull(EcRepository.cacheBacking[id], "L1 should hold the null marker.");
145
+ delete EcRepository.cache[id];
146
+ });
147
+ });
148
+
149
+ describe("clearCache", () => {
150
+ it('clears both L1 and L2 when L2 caching is enabled', async () => {
151
+ EcRepository.cachingL2 = true;
152
+ let vId = versionedId();
153
+ let uId = unversionedId();
154
+ EcRepository.cache[vId] = makeThing(vId);
155
+ EcRepository.cache[uId] = makeThing(uId);
156
+ assert.isAbove(await l2Count(), 0);
157
+ EcRepository.clearCache();
158
+ assert.equal(Object.keys(EcRepository.cacheBacking).length, 0);
159
+ assert.equal(await l2Count(), 0, "IndexedDB store not emptied.");
160
+ assert.isUndefined(await EcRepository.cacheGet(vId));
161
+ assert.isUndefined(await EcRepository.cacheGet(uId));
162
+ });
163
+ it('clears only L1 when L2 caching is disabled', async () => {
164
+ EcRepository.cachingL2 = true;
165
+ let id = versionedId();
166
+ EcRepository.cache[id] = makeThing(id);
167
+ EcRepository.cachingL2 = false;
168
+ EcRepository.clearCache();
169
+ assert.equal(Object.keys(EcRepository.cacheBacking).length, 0);
170
+ assert.isNotNull(await l2Get(id), "IndexedDB copy should survive an L1-only clear.");
171
+ // The surviving L2 copy is served again once L2 caching is re-enabled.
172
+ EcRepository.cachingL2 = true;
173
+ let got = await EcRepository.cacheGet(id);
174
+ assert.instanceOf(got, EcRemoteLinkedData);
175
+ assert.equal(got.id, id);
176
+ EcRepository.clearCache();
177
+ });
178
+ });
179
+ });
@@ -9,128 +9,6 @@ const EcIdentityManager = require('../org/cassproject/ebac/identity/EcIdentityMa
9
9
  const { expect } = chai;
10
10
 
11
11
  describe('EcLevel', () => {
12
- let sandbox;
13
-
14
- beforeEach(() => {
15
- sandbox = sinon.createSandbox();
16
- });
17
-
18
- afterEach(() => {
19
- sandbox.restore();
20
- });
21
-
22
- describe('get', () => {
23
- it('should retrieve a level by ID', (done) => {
24
- const id = 'testId';
25
- const success = sinon.spy();
26
- const failure = sinon.spy();
27
- const repo = {};
28
- const eim = {};
29
-
30
- sandbox.stub(EcRepository, 'getAs').callsFake((id, level, successCallback, failureCallback, repo, eim) => {
31
- successCallback(level);
32
- });
33
-
34
- EcLevel.get(id, success, failure, repo, eim);
35
-
36
- expect(success.calledOnce).to.be.true;
37
- expect(failure.called).to.be.false;
38
- done();
39
- });
40
- });
41
-
42
- describe('getBlocking', () => {
43
- it('should retrieve a level by ID synchronously', () => {
44
- const id = 'testId';
45
- const repo = {};
46
- const eim = {};
47
-
48
- sandbox.stub(EcRepository, 'getAs').returns(new EcLevel());
49
-
50
- const result = EcLevel.getBlocking(id, repo, eim);
51
-
52
- expect(result).to.be.instanceOf(EcLevel);
53
- });
54
- });
55
-
56
- describe('search', () => {
57
- it('should search for levels with a query', (done) => {
58
- const repo = {};
59
- const query = 'testQuery';
60
- const success = sinon.spy();
61
- const failure = sinon.spy();
62
- const paramObj = {};
63
- const eim = {};
64
-
65
- sandbox.stub(EcRepository, 'searchAs').callsFake((repo, query, levelFactory, successCallback, failureCallback, paramObj, eim) => {
66
- successCallback([new EcLevel()]);
67
- });
68
-
69
- EcLevel.search(repo, query, success, failure, paramObj, eim);
70
-
71
- expect(success.calledOnce).to.be.true;
72
- expect(failure.called).to.be.false;
73
- done();
74
- });
75
- });
76
-
77
- describe('searchByCompetency', () => {
78
- it('should search for levels by competency ID', (done) => {
79
- const repo = {};
80
- const competencyId = 'testCompetencyId';
81
- const success = sinon.spy();
82
- const failure = sinon.spy();
83
- const paramObj = {};
84
- const eim = {};
85
-
86
- sandbox.stub(EcLevel, 'search').callsFake((repo, query, successCallback, failureCallback, paramObj, eim) => {
87
- successCallback([new EcLevel()]);
88
- });
89
-
90
- EcLevel.searchByCompetency(repo, competencyId, success, failure, paramObj, eim);
91
-
92
- expect(success.calledOnce).to.be.true;
93
- expect(failure.called).to.be.false;
94
- done();
95
- });
96
-
97
- it('should call failure callback if no competency ID is provided', () => {
98
- const repo = {};
99
- const competencyId = '';
100
- const success = sinon.spy();
101
- const failure = sinon.spy();
102
- const paramObj = {};
103
- const eim = {};
104
-
105
- EcLevel.searchByCompetency(repo, competencyId, success, failure, paramObj, eim);
106
-
107
- expect(failure.calledOnce).to.be.true;
108
- expect(success.called).to.be.false;
109
- });
110
- });
111
-
112
- describe('addRelationship', () => {
113
- it('should add a relationship between levels', async () => {
114
- const level = new EcLevel();
115
- level.id = 'sourceId';
116
- const targetLevel = new EcLevel();
117
- targetLevel.id = 'targetId';
118
- const alignmentType = 'testType';
119
- const identity = EcPpk.generateKey();
120
- const success = sinon.spy();
121
- const failure = sinon.spy();
122
- const repo = new EcRepository();
123
- await repo.init(process.env.CASS_LOOPBACK || 'http://localhost/api/');
124
- const eim = EcIdentityManager.default;
125
-
126
- let a = await level.addRelationship(targetLevel, alignmentType, identity, success, failure, repo, eim);
127
-
128
- expect(a.source).to.equal(level.id);
129
- expect(a.target).to.equal(targetLevel.id);
130
- expect(a.relationType).to.equal(alignmentType);
131
- });
132
- });
133
-
134
12
  describe('setName', () => {
135
13
  it('should set the name of the level', () => {
136
14
  const level = new EcLevel();