@researchdatabox/sails-hook-redbox-storage-mongo 1.4.8 → 1.5.0
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/.github/dependabot.yml +12 -0
- package/README.md +0 -0
- package/api/models/DeletedRecord.js +0 -0
- package/api/models/Record.js +0 -0
- package/api/models/RecordAudit.js +0 -0
- package/api/services/MongoStorageService.js +42 -38
- package/config/record.js +0 -0
- package/config/storage.js +0 -0
- package/index.js +0 -0
- package/package.json +30 -18
- package/test/unit/.mocharc.js +21 -0
- package/test/unit/bootstrap.js +63 -0
- package/test/unit/db/storage.test.js +121 -0
- package/transformer/ExportJSONTransformer.js +2 -2
- package/tsconfig.json +0 -0
- package/typescript/api/services/MongoStorageService.ts +63 -26
- package/typescript/transformer/ExportJSONTransformer.ts +5 -5
- package/test/bootstrap.js +0 -65
package/README.md
CHANGED
|
File without changes
|
|
File without changes
|
package/api/models/Record.js
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -12,8 +12,9 @@ var __await = (this && this.__await) || function (v) { return this instanceof __
|
|
|
12
12
|
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
|
13
13
|
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
14
14
|
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
15
|
-
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
16
|
-
function
|
|
15
|
+
return i = {}, verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
16
|
+
function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
|
|
17
|
+
function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
|
|
17
18
|
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
|
18
19
|
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
19
20
|
function fulfill(value) { resume("next", value); }
|
|
@@ -24,7 +25,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
24
25
|
exports.Services = void 0;
|
|
25
26
|
const Rx_1 = require("rxjs/Rx");
|
|
26
27
|
const uuid_1 = require("uuid");
|
|
27
|
-
const
|
|
28
|
+
const luxon_1 = require("luxon");
|
|
28
29
|
const mongodb = require("mongodb");
|
|
29
30
|
const util = require("util");
|
|
30
31
|
const stream = require("stream");
|
|
@@ -146,12 +147,19 @@ var Services;
|
|
|
146
147
|
_.unset(record, 'dateCreated');
|
|
147
148
|
_.unset(record, 'lastSaveDate');
|
|
148
149
|
_.unset(record, '_id');
|
|
150
|
+
_.unset(record, 'id');
|
|
149
151
|
yield Record.updateOne({ redboxOid: oid }).set(record);
|
|
150
152
|
response.success = true;
|
|
151
153
|
}
|
|
152
154
|
catch (err) {
|
|
153
|
-
sails.log.error(`${this.logHeader} Failed to save update to MongoDB
|
|
154
|
-
|
|
155
|
+
sails.log.error(`${this.logHeader} Failed to save update to MongoDB: ${JSON.stringify({
|
|
156
|
+
error: err,
|
|
157
|
+
response: response,
|
|
158
|
+
brand: brand,
|
|
159
|
+
oid: oid,
|
|
160
|
+
record: record,
|
|
161
|
+
user: user,
|
|
162
|
+
})}`);
|
|
155
163
|
response.success = false;
|
|
156
164
|
response.message = err;
|
|
157
165
|
}
|
|
@@ -198,28 +206,24 @@ var Services;
|
|
|
198
206
|
provideUserAccessAndRemovePendingAccess(oid, userid, pendingValue) {
|
|
199
207
|
const batchFn = () => __awaiter(this, void 0, void 0, function* () {
|
|
200
208
|
const metadata = yield this.getMeta(oid);
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
metadata
|
|
211
|
-
metadata
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
metadata['authorization']['viewPending'] = pendingViewArray;
|
|
222
|
-
metadata['authorization']['view'] = viewArray;
|
|
209
|
+
const pendingEditArray = _.get(metadata, 'authorization.editPending', []);
|
|
210
|
+
const editArray = _.get(metadata, 'authorization.edit', []);
|
|
211
|
+
const pendingEditArrayFiltered = pendingEditArray.filter(value => value !== pendingValue);
|
|
212
|
+
const pendingEditFound = pendingEditArray.length > pendingEditArrayFiltered.length;
|
|
213
|
+
if (pendingEditFound && !editArray.includes(userid)) {
|
|
214
|
+
editArray.push(userid);
|
|
215
|
+
}
|
|
216
|
+
_.set(metadata, 'authorization.editPending', pendingEditArrayFiltered);
|
|
217
|
+
_.set(metadata, 'authorization.edit', editArray);
|
|
218
|
+
const pendingViewArray = _.get(metadata, 'authorization.viewPending', []);
|
|
219
|
+
const viewArray = _.get(metadata, 'authorization.view', []);
|
|
220
|
+
const pendingViewArrayFiltered = pendingViewArray.filter(value => value !== pendingValue);
|
|
221
|
+
const pendingViewFound = pendingViewArray.length > pendingViewArrayFiltered.length;
|
|
222
|
+
if (pendingViewFound && !viewArray.includes(userid)) {
|
|
223
|
+
viewArray.push(userid);
|
|
224
|
+
}
|
|
225
|
+
_.set(metadata, 'authorization.viewPending', pendingViewArrayFiltered);
|
|
226
|
+
_.set(metadata, 'authorization.view', viewArray);
|
|
223
227
|
try {
|
|
224
228
|
yield this.updateMeta(null, oid, metadata);
|
|
225
229
|
}
|
|
@@ -230,8 +234,8 @@ var Services;
|
|
|
230
234
|
});
|
|
231
235
|
batchFn();
|
|
232
236
|
}
|
|
233
|
-
getRelatedRecords(
|
|
234
|
-
return __awaiter(this,
|
|
237
|
+
getRelatedRecords(oid_1, brand_1) {
|
|
238
|
+
return __awaiter(this, arguments, void 0, function* (oid, brand, recordTypeName = null, mappingContext = null) {
|
|
235
239
|
let record = yield this.getMeta(oid);
|
|
236
240
|
if (_.isEmpty(recordTypeName)) {
|
|
237
241
|
recordTypeName = record['metaMetadata']['type'];
|
|
@@ -283,8 +287,8 @@ var Services;
|
|
|
283
287
|
return mappingContext;
|
|
284
288
|
});
|
|
285
289
|
}
|
|
286
|
-
delete(
|
|
287
|
-
return __awaiter(this,
|
|
290
|
+
delete(oid_1) {
|
|
291
|
+
return __awaiter(this, arguments, void 0, function* (oid, permanentlyDelete = false) {
|
|
288
292
|
const response = new redbox_core_types_1.StorageServiceResponse();
|
|
289
293
|
try {
|
|
290
294
|
if (permanentlyDelete) {
|
|
@@ -332,7 +336,7 @@ var Services;
|
|
|
332
336
|
const logName = _.get(options, 'logName', null);
|
|
333
337
|
if (logName) {
|
|
334
338
|
let log = _.get(record, logName, null);
|
|
335
|
-
const entry = { date:
|
|
339
|
+
const entry = { date: luxon_1.DateTime.now().toFormat("yyyy-LL-dd'T'HH:mm:ss") };
|
|
336
340
|
if (log) {
|
|
337
341
|
log.push(entry);
|
|
338
342
|
}
|
|
@@ -366,8 +370,8 @@ var Services;
|
|
|
366
370
|
return record;
|
|
367
371
|
});
|
|
368
372
|
}
|
|
369
|
-
getDeletedRecords(
|
|
370
|
-
return __awaiter(this,
|
|
373
|
+
getDeletedRecords(workflowState_1) {
|
|
374
|
+
return __awaiter(this, arguments, void 0, function* (workflowState, recordType = undefined, start, rows = 10, username, roles, brand, editAccessOnly = undefined, packageType = undefined, sort = undefined, filterFields = undefined, filterString = undefined, filterMode = 'regex') {
|
|
371
375
|
let query = {
|
|
372
376
|
"deletedRecordMetadata.metaMetadata.brandId": brand.id
|
|
373
377
|
};
|
|
@@ -445,8 +449,8 @@ var Services;
|
|
|
445
449
|
return response;
|
|
446
450
|
});
|
|
447
451
|
}
|
|
448
|
-
getRecords(
|
|
449
|
-
return __awaiter(this,
|
|
452
|
+
getRecords(workflowState_1) {
|
|
453
|
+
return __awaiter(this, arguments, void 0, function* (workflowState, recordType = undefined, start, rows = 10, username, roles, brand, editAccessOnly = undefined, packageType = undefined, sort = undefined, filterFields = undefined, filterString = undefined, filterMode = undefined, secondarySort = undefined) {
|
|
450
454
|
if (_.isUndefined(filterMode) || _.isNull(filterMode) || _.isEmpty(filterMode)) {
|
|
451
455
|
filterMode = 'regex';
|
|
452
456
|
}
|
|
@@ -562,8 +566,8 @@ var Services;
|
|
|
562
566
|
return { items: yield this.deletedRecordCol.find(query, options).toArray(), totalItems: yield this.deletedRecordCol.count(query) };
|
|
563
567
|
});
|
|
564
568
|
}
|
|
565
|
-
fetchAllRecords(
|
|
566
|
-
return __asyncGenerator(this, arguments, function* fetchAllRecords_1() {
|
|
569
|
+
fetchAllRecords(query_1, options_1) {
|
|
570
|
+
return __asyncGenerator(this, arguments, function* fetchAllRecords_1(query, options, stringifyJSON = false) {
|
|
567
571
|
let skip = 0;
|
|
568
572
|
let limit = options.limit;
|
|
569
573
|
options.skip = skip;
|
package/config/record.js
CHANGED
|
File without changes
|
package/config/storage.js
CHANGED
|
File without changes
|
package/index.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@researchdatabox/sails-hook-redbox-storage-mongo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "A MongoDB storage plugin for ReDBox.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"sails": {
|
|
@@ -11,9 +11,16 @@
|
|
|
11
11
|
"url": "https://github.com/redbox-mint/sails-hook-redbox-storage-mongo.git"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
14
|
+
"compile": "./node_modules/typescript/bin/tsc -p tsconfig.json",
|
|
15
|
+
"dev:run": "docker compose -f ./support/docker-compose.yml -f ./support/docker-compose.dev.yml up --menu=false --remove-orphans --abort-on-container-exit --exit-code-from rbportal",
|
|
16
|
+
"predev:run": "npm install --ignore-scripts && npm run compile",
|
|
17
|
+
"dev:clean": "docker compose -f ./support/docker-compose.yml -f ./support/docker-compose.dev.yml down",
|
|
18
|
+
"dev:logs": "docker compose -f ./support/docker-compose.yml -f ./support/docker-compose.dev.yml logs",
|
|
19
|
+
"dev:removedata": "docker compose -f ./support/docker-compose.yml -f ./support/docker-compose.dev.yml down -v",
|
|
20
|
+
"test": "docker compose -f ./support/docker-compose.yml -f ./support/docker-compose.mocha.yml up --menu=false --abort-on-container-exit --exit-code-from rbportal",
|
|
21
|
+
"pretest": "npm install --ignore-scripts && npm run compile",
|
|
22
|
+
"posttest": "npm run test:clean",
|
|
23
|
+
"test:clean": "docker compose -f ./support/docker-compose.yml -f ./support/docker-compose.mocha.yml down -v"
|
|
17
24
|
},
|
|
18
25
|
"keywords": [
|
|
19
26
|
"sails",
|
|
@@ -26,27 +33,32 @@
|
|
|
26
33
|
"author": "QCIF Engineering dev@redboxresearchdata.com.au",
|
|
27
34
|
"license": "GPL-3.0",
|
|
28
35
|
"devDependencies": {
|
|
29
|
-
"@researchdatabox/redbox-core-types": "
|
|
30
|
-
"@types/chai": "
|
|
31
|
-
"@types/lodash": "
|
|
32
|
-
"@types/mocha": "
|
|
33
|
-
"
|
|
36
|
+
"@researchdatabox/redbox-core-types": "1.4.8",
|
|
37
|
+
"@types/chai": "5.2.2",
|
|
38
|
+
"@types/lodash": "4.17.17",
|
|
39
|
+
"@types/mocha": "10.0.10",
|
|
40
|
+
"@types/node": "20.17.51",
|
|
41
|
+
"chai": "5.2.0",
|
|
34
42
|
"fs": "0.0.1-security",
|
|
35
|
-
"mocha": "
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
"rxjs
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
43
|
+
"mocha": "11.5.0",
|
|
44
|
+
"mocha-junit-reporter": "2.2.1",
|
|
45
|
+
"luxon": "^3.7.1",
|
|
46
|
+
"mongodb": "6.16.0",
|
|
47
|
+
"rxjs": "6.6.7",
|
|
48
|
+
"rxjs-compat": "6.6.7",
|
|
49
|
+
"sails": "1.5.14",
|
|
50
|
+
"stream": "0.0.3",
|
|
51
|
+
"typescript": "5.5.4",
|
|
52
|
+
"util": "0.12.5"
|
|
44
53
|
},
|
|
45
54
|
"dependencies": {
|
|
46
55
|
"json2csv": "^5.0.3",
|
|
47
56
|
"lodash": "^4.17.21",
|
|
48
57
|
"uuid": "8.3.0"
|
|
49
58
|
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"luxon": ">=3.0.0"
|
|
61
|
+
},
|
|
50
62
|
"publishConfig": {
|
|
51
63
|
"access": "public"
|
|
52
64
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
require: "chai",
|
|
5
|
+
recursive: true,
|
|
6
|
+
slow: 2000,
|
|
7
|
+
timeout: "20s",
|
|
8
|
+
ui: "bdd",
|
|
9
|
+
global: ["sails", "moment", "_", "expect"],
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
if (process.env.CI === "true") {
|
|
13
|
+
console.log("Mocha running in CI.");
|
|
14
|
+
// (For CI) Run mocha and write results to a junit format file:
|
|
15
|
+
module.exports["reporter"] = "mocha-junit-reporter";
|
|
16
|
+
module.exports["reporter-option"] = "mochaFile=/opt/redbox-portal/node_modules/@researchdatabox/sails-hook-redbox-storage-mongo/support/junit/unit-mocha.xml";
|
|
17
|
+
} else {
|
|
18
|
+
console.log("Mocha running in local dev.");
|
|
19
|
+
// (For development) Run mocha and show the results on stdout:
|
|
20
|
+
module.exports["reporter"] = "spec";
|
|
21
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
var sails = require("sails");
|
|
2
|
+
var _ = require("lodash");
|
|
3
|
+
|
|
4
|
+
global.moment = require("moment");
|
|
5
|
+
global.sails = sails;
|
|
6
|
+
global._ = _;
|
|
7
|
+
|
|
8
|
+
/* global before, after */
|
|
9
|
+
|
|
10
|
+
before(function (done) {
|
|
11
|
+
import("chai").then((chai) => {
|
|
12
|
+
global.chai = chai;
|
|
13
|
+
global.should = chai.should();
|
|
14
|
+
global.expect = chai.expect;
|
|
15
|
+
|
|
16
|
+
// Set the timeout so that Sails has enough time to lift.
|
|
17
|
+
this.timeout(60000);
|
|
18
|
+
sails.lift({
|
|
19
|
+
log: {
|
|
20
|
+
level: "verbose",
|
|
21
|
+
},
|
|
22
|
+
hooks: {
|
|
23
|
+
grunt: false,
|
|
24
|
+
},
|
|
25
|
+
models: {
|
|
26
|
+
datastore: "mongodb",
|
|
27
|
+
migrate: "drop",
|
|
28
|
+
},
|
|
29
|
+
datastores: {
|
|
30
|
+
mongodb: {
|
|
31
|
+
adapter: require('sails-mongo'),
|
|
32
|
+
url: "mongodb://mongodb:27017/redbox-portal"
|
|
33
|
+
},
|
|
34
|
+
redboxStorage: {
|
|
35
|
+
adapter: require('sails-mongo'),
|
|
36
|
+
url: 'mongodb://mongodb:27017/redbox-storage'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
security: {
|
|
40
|
+
csrf: false,
|
|
41
|
+
},
|
|
42
|
+
auth: {
|
|
43
|
+
default: {
|
|
44
|
+
local: {
|
|
45
|
+
default: {
|
|
46
|
+
token: "auth-default-local-default-token",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
}, function (err, server) {
|
|
52
|
+
if (err) return done(err);
|
|
53
|
+
done(err, sails);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
after(function (done) {
|
|
59
|
+
// here you can clear fixtures, etc.
|
|
60
|
+
if (sails && _.isFunction(sails.lower)) {
|
|
61
|
+
sails.lower(done);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const service = require("../../../api/services/MongoStorageService");
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/* global describe, it, expect */
|
|
5
|
+
|
|
6
|
+
async function sleep(ms = 300) {
|
|
7
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe("Records", function () {
|
|
11
|
+
[
|
|
12
|
+
{
|
|
13
|
+
// missing 'editPending' does not cause error
|
|
14
|
+
args: {
|
|
15
|
+
record: {
|
|
16
|
+
metadata: {},
|
|
17
|
+
"authorization": {
|
|
18
|
+
"viewRoles": ["Admin", "Librarians"],
|
|
19
|
+
"editRoles": ["Admin", "Librarians"],
|
|
20
|
+
"view": ["example-2@redboxresearchdata.com.au"],
|
|
21
|
+
"edit": [],
|
|
22
|
+
"viewPending": ["example-1@redboxresearchdata.com.au"]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
expected: {
|
|
27
|
+
record: {
|
|
28
|
+
"authorization": {
|
|
29
|
+
"viewRoles": ["Admin", "Librarians"],
|
|
30
|
+
"editRoles": ["Admin", "Librarians"],
|
|
31
|
+
"view": ["example-2@redboxresearchdata.com.au", "example-1@redboxresearchdata.com.au"],
|
|
32
|
+
"edit": [],
|
|
33
|
+
"viewPending": [],
|
|
34
|
+
"editPending": [],
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
// duplicate entries in pending array are transformed to in one entry in real access array
|
|
41
|
+
args: {
|
|
42
|
+
record: {
|
|
43
|
+
metadata: {},
|
|
44
|
+
"authorization": {
|
|
45
|
+
"viewRoles": ["Admin", "Librarians"],
|
|
46
|
+
"editRoles": ["Admin", "Librarians"],
|
|
47
|
+
"view": ["example-2@redboxresearchdata.com.au"],
|
|
48
|
+
"edit": [],
|
|
49
|
+
"viewPending": [],
|
|
50
|
+
"editPending": ["example-1@redboxresearchdata.com.au", "example-1@redboxresearchdata.com.au"],
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
expected: {
|
|
55
|
+
record: {
|
|
56
|
+
"authorization": {
|
|
57
|
+
"viewRoles": ["Admin", "Librarians"],
|
|
58
|
+
"editRoles": ["Admin", "Librarians"],
|
|
59
|
+
"view": ["example-2@redboxresearchdata.com.au"],
|
|
60
|
+
"edit": ["example-1@redboxresearchdata.com.au"],
|
|
61
|
+
"viewPending": [],
|
|
62
|
+
"editPending": [],
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
// same entry in real access array and pending is removed from pending array
|
|
69
|
+
args: {
|
|
70
|
+
record: {
|
|
71
|
+
metadata: {},
|
|
72
|
+
"authorization": {
|
|
73
|
+
"viewRoles": ["Admin", "Librarians"],
|
|
74
|
+
"editRoles": ["Admin", "Librarians"],
|
|
75
|
+
"view": ["example-2@redboxresearchdata.com.au"],
|
|
76
|
+
"edit": ["example-1@redboxresearchdata.com.au"],
|
|
77
|
+
"viewPending": [],
|
|
78
|
+
"editPending": ["example-1@redboxresearchdata.com.au"],
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
expected: {
|
|
83
|
+
record: {
|
|
84
|
+
"authorization": {
|
|
85
|
+
"viewRoles": ["Admin", "Librarians"],
|
|
86
|
+
"editRoles": ["Admin", "Librarians"],
|
|
87
|
+
"view": ["example-2@redboxresearchdata.com.au"],
|
|
88
|
+
"edit": ["example-1@redboxresearchdata.com.au"],
|
|
89
|
+
"viewPending": [],
|
|
90
|
+
"editPending": [],
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
].forEach(({args, expected}) => {
|
|
96
|
+
it(`should provideUserAccessAndRemovePendingAccess as expected '${JSON.stringify(args)}' = ${JSON.stringify(expected)}`, async function () {
|
|
97
|
+
|
|
98
|
+
const brand = 'testing-brand';
|
|
99
|
+
const record = args.record;
|
|
100
|
+
const recordType = "rdmp";
|
|
101
|
+
|
|
102
|
+
const response = await service.create(brand, record, recordType);
|
|
103
|
+
|
|
104
|
+
expect(response.oid).to.have.lengthOf(32);
|
|
105
|
+
|
|
106
|
+
const oid = response.oid;
|
|
107
|
+
const userid = "example-1@redboxresearchdata.com.au";
|
|
108
|
+
const pendingValue = "example-1@redboxresearchdata.com.au";
|
|
109
|
+
|
|
110
|
+
service.provideUserAccessAndRemovePendingAccess(oid, userid, pendingValue);
|
|
111
|
+
// TODO: provideUserAccessAndRemovePendingAccess is a sync method, but it calls async methods.
|
|
112
|
+
// So, wait for a bit for the method to complete.
|
|
113
|
+
// This means that any errors may not be raised, as they occur in an untracked async function.
|
|
114
|
+
await sleep(5000);
|
|
115
|
+
|
|
116
|
+
const result = await service.getMeta(oid);
|
|
117
|
+
|
|
118
|
+
expect(result.authorization).to.eql(expected.record.authorization);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const { Transform } = require('stream');
|
|
4
|
-
const
|
|
4
|
+
const luxon_1 = require("luxon");
|
|
5
5
|
class ExportJSONTransformer extends Transform {
|
|
6
6
|
constructor(recordtype, modifiedBefore, modifiedAfter) {
|
|
7
7
|
super();
|
|
8
8
|
this.first = true;
|
|
9
|
-
this.push(`{ "recordType": "${recordtype}",\n "dateGenerated": "${
|
|
9
|
+
this.push(`{ "recordType": "${recordtype}",\n "dateGenerated": "${luxon_1.DateTime.now().toISO()}",\n`);
|
|
10
10
|
if (!_.isEmpty(modifiedBefore) || !_.isEmpty(modifiedAfter)) {
|
|
11
11
|
this.push(`"dateModifiedRange": ${this.getDateModifiedRangeObject(modifiedBefore, modifiedAfter)},\n`);
|
|
12
12
|
}
|
package/tsconfig.json
CHANGED
|
File without changes
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
|
|
5
5
|
import { Sails, Model } from "sails";
|
|
6
6
|
import { v1 as uuidv1 } from 'uuid';
|
|
7
|
-
import
|
|
7
|
+
import { DateTime } from 'luxon';
|
|
8
8
|
|
|
9
9
|
import mongodb = require('mongodb');
|
|
10
10
|
import util = require('util');
|
|
@@ -153,11 +153,23 @@ export module Services {
|
|
|
153
153
|
_.unset(record, 'dateCreated');
|
|
154
154
|
_.unset(record, 'lastSaveDate');
|
|
155
155
|
_.unset(record, '_id');
|
|
156
|
+
|
|
157
|
+
// Records with an 'id' property fail to save with error:
|
|
158
|
+
// {"cause":{"name":"AdapterError","adapterMethodName":"update","modelIdentity":"record","raw":{"code":"E_CANNOT_INTERPRET_AS_OBJECTID"}},"isOperational":true}
|
|
159
|
+
// Fix by ensuring the 'id' property is removed.
|
|
160
|
+
_.unset(record, 'id');
|
|
161
|
+
|
|
156
162
|
await Record.updateOne({ redboxOid: oid }).set(record);
|
|
157
163
|
response.success = true;
|
|
158
164
|
} catch (err) {
|
|
159
|
-
sails.log.error(`${this.logHeader} Failed to save update to MongoDB
|
|
160
|
-
|
|
165
|
+
sails.log.error(`${this.logHeader} Failed to save update to MongoDB: ${JSON.stringify({
|
|
166
|
+
error: err,
|
|
167
|
+
response: response,
|
|
168
|
+
brand: brand,
|
|
169
|
+
oid: oid,
|
|
170
|
+
record: record,
|
|
171
|
+
user: user,
|
|
172
|
+
})}`);
|
|
161
173
|
response.success = false;
|
|
162
174
|
response.message = err;
|
|
163
175
|
}
|
|
@@ -202,33 +214,55 @@ export module Services {
|
|
|
202
214
|
return response;
|
|
203
215
|
}
|
|
204
216
|
|
|
217
|
+
/**
|
|
218
|
+
* If pendingValue is in editPending or viewPending, remove all instances from the pending arrays and
|
|
219
|
+
* put the userid in the matching access / non-pending array, then save the changes to the storage.
|
|
220
|
+
*
|
|
221
|
+
* Implementation Note: This is a sync method, but it calls async methods.
|
|
222
|
+
* This means that the storage will not be updated when this method returns, but at some point later.
|
|
223
|
+
*
|
|
224
|
+
* @param oid The record identifier.
|
|
225
|
+
* @param userid The value to put in the matching access / non-pending arrays.
|
|
226
|
+
* @param pendingValue The value to find in the pending arrays.
|
|
227
|
+
*/
|
|
205
228
|
public provideUserAccessAndRemovePendingAccess(oid, userid, pendingValue): void {
|
|
206
229
|
const batchFn = async () => {
|
|
207
230
|
const metadata = await this.getMeta(oid);
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
231
|
+
|
|
232
|
+
// Update edit authorization - remove pending edit access and add real edit access with userid
|
|
233
|
+
const pendingEditArray: string[] = _.get(metadata, 'authorization.editPending', []);
|
|
234
|
+
const editArray: string[] = _.get(metadata, 'authorization.edit', []);
|
|
235
|
+
|
|
236
|
+
// remove all items matching pendingValue from the pendingEditArray
|
|
237
|
+
const pendingEditArrayFiltered = pendingEditArray.filter(value => value !== pendingValue);
|
|
238
|
+
const pendingEditFound = pendingEditArray.length > pendingEditArrayFiltered.length;
|
|
239
|
+
|
|
240
|
+
// add the item to the editArray if it existed in the pendingEditArray, and it is not in the editArray
|
|
241
|
+
if (pendingEditFound && !editArray.includes(userid)) {
|
|
242
|
+
editArray.push(userid);
|
|
217
243
|
}
|
|
218
|
-
|
|
219
|
-
metadata
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
244
|
+
|
|
245
|
+
// update the metadata with the modified arrays
|
|
246
|
+
_.set(metadata, 'authorization.editPending', pendingEditArrayFiltered);
|
|
247
|
+
_.set(metadata, 'authorization.edit', editArray);
|
|
248
|
+
|
|
249
|
+
// Update view authorization - remove pending view access and add real view access with userid
|
|
250
|
+
const pendingViewArray: string[] = _.get(metadata, 'authorization.viewPending', []);
|
|
251
|
+
const viewArray: string[] = _.get(metadata, 'authorization.view', []);
|
|
252
|
+
|
|
253
|
+
// remove all items matching pendingValue from the pendingViewArray
|
|
254
|
+
const pendingViewArrayFiltered = pendingViewArray.filter(value => value !== pendingValue);
|
|
255
|
+
const pendingViewFound = pendingViewArray.length > pendingViewArrayFiltered.length;
|
|
256
|
+
|
|
257
|
+
// add the item to the viewArray if it existed in the pendingViewArray, and it is not in the viewArray
|
|
258
|
+
if (pendingViewFound && !viewArray.includes(userid)) {
|
|
259
|
+
viewArray.push(userid);
|
|
229
260
|
}
|
|
230
|
-
|
|
231
|
-
metadata
|
|
261
|
+
|
|
262
|
+
// update the metadata with the modified arrays
|
|
263
|
+
_.set(metadata, 'authorization.viewPending', pendingViewArrayFiltered);
|
|
264
|
+
_.set(metadata, 'authorization.view', viewArray);
|
|
265
|
+
|
|
232
266
|
try {
|
|
233
267
|
await this.updateMeta(null, oid, metadata);
|
|
234
268
|
} catch (err) {
|
|
@@ -236,6 +270,8 @@ export module Services {
|
|
|
236
270
|
sails.log.error(JSON.stringify(err));
|
|
237
271
|
}
|
|
238
272
|
};
|
|
273
|
+
|
|
274
|
+
// TODO: Fix this so that the method only returns after the storage is updated.
|
|
239
275
|
batchFn();
|
|
240
276
|
}
|
|
241
277
|
|
|
@@ -339,7 +375,8 @@ export module Services {
|
|
|
339
375
|
const logName = _.get(options, 'logName', null);
|
|
340
376
|
if (logName) {
|
|
341
377
|
let log = _.get(record, logName, null);
|
|
342
|
-
|
|
378
|
+
// Use Luxon for ISO-like local timestamp without timezone offset
|
|
379
|
+
const entry = { date: DateTime.now().toFormat("yyyy-LL-dd'T'HH:mm:ss") };
|
|
343
380
|
if (log) {
|
|
344
381
|
log.push(entry);
|
|
345
382
|
} else {
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const { Transform } = require('stream');
|
|
4
|
+
import { DateTime } from 'luxon';
|
|
5
|
+
declare var require: any;
|
|
6
|
+
declare var module: any;
|
|
7
7
|
declare let sails, _;
|
|
8
8
|
|
|
9
9
|
class ExportJSONTransformer extends Transform {
|
|
10
10
|
first:boolean = true;
|
|
11
11
|
constructor(recordtype:string, modifiedBefore:string, modifiedAfter:string) {
|
|
12
12
|
super();
|
|
13
|
-
|
|
13
|
+
this.push(`{ "recordType": "${recordtype}",\n "dateGenerated": "${DateTime.now().toISO()}",\n`)
|
|
14
14
|
if(!_.isEmpty(modifiedBefore) || !_.isEmpty(modifiedAfter)) {
|
|
15
15
|
this.push(`"dateModifiedRange": ${this.getDateModifiedRangeObject(modifiedBefore, modifiedAfter)},\n`);
|
|
16
16
|
}
|
package/test/bootstrap.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
var Sails = require('sails').Sails;
|
|
2
|
-
|
|
3
|
-
describe('Bootstrap tests ::', function() {
|
|
4
|
-
// Var to hold a running sails app instance
|
|
5
|
-
var _sails;
|
|
6
|
-
var appPath = '/opt/redbox-portal'
|
|
7
|
-
|
|
8
|
-
// Before running any tests, attempt to lift Sails
|
|
9
|
-
before(function (done) {
|
|
10
|
-
|
|
11
|
-
// Hook will timeout in 10 seconds
|
|
12
|
-
this.timeout(11000);
|
|
13
|
-
|
|
14
|
-
// Attempt to lift sails
|
|
15
|
-
Sails().lift({
|
|
16
|
-
appPath: appPath,
|
|
17
|
-
models: {
|
|
18
|
-
migrate: 'drop'
|
|
19
|
-
},
|
|
20
|
-
datastores: {
|
|
21
|
-
mongodb: {
|
|
22
|
-
adapter: require(appPath+'/node_modules/sails-mongo'),
|
|
23
|
-
url: "mongodb://mongodb:27017/redbox-portal"
|
|
24
|
-
},
|
|
25
|
-
redboxStorage: {
|
|
26
|
-
adapter: require(appPath+'/node_modules/sails-mongo'),
|
|
27
|
-
url: 'mongodb://mongodb:27017/redbox-storage'
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
globals: {
|
|
31
|
-
sails: true,
|
|
32
|
-
_: require('lodash'),
|
|
33
|
-
async: require('async'),
|
|
34
|
-
models: true,
|
|
35
|
-
services: true
|
|
36
|
-
},
|
|
37
|
-
hooks: {
|
|
38
|
-
// Skip grunt (unless your hook uses it)
|
|
39
|
-
"grunt": false
|
|
40
|
-
},
|
|
41
|
-
log: {level: "verbose"}
|
|
42
|
-
},function (err, __sails) {
|
|
43
|
-
if (err) return done(err);
|
|
44
|
-
_sails = __sails;
|
|
45
|
-
return done();
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// After tests are complete, lower Sails
|
|
50
|
-
after(function (done) {
|
|
51
|
-
|
|
52
|
-
// Lower Sails (if it successfully lifted)
|
|
53
|
-
if (_sails) {
|
|
54
|
-
return _sails.lower(done);
|
|
55
|
-
}
|
|
56
|
-
// Otherwise just return
|
|
57
|
-
return done();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Test that Sails can lift with the hook in place
|
|
61
|
-
it ('sails does not crash', function() {
|
|
62
|
-
return true;
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
});
|