Skip to content

Commit 50e4886

Browse files
authored
feat(Mongo): Update xAPI service to use mongodb node.js driver v4 (LLC-1689) (#862)
1 parent dfb79ee commit 50e4886

File tree

35 files changed

+265
-180
lines changed

35 files changed

+265
-180
lines changed

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"jsonwebtoken": "^8.5.1",
6161
"lodash": "^4.17.4",
6262
"mime-types": "2.1.35",
63-
"mongodb": "^3.0.2",
63+
"mongodb": "^4.10.0",
6464
"node-fetch": "^2.0.0",
6565
"object-hash": "^2.0.0",
6666
"patch-package": "^6.4.7",
@@ -88,7 +88,6 @@
8888
"@types/lodash": "4.14.176",
8989
"@types/mime-types": "2.1.1",
9090
"@types/mocha": "8.2.3",
91-
"@types/mongodb": "3.6.20",
9291
"@types/node": "^14.18.18",
9392
"@types/node-fetch": "2.5.8",
9493
"@types/object-hash": "1.3.4",

src/apps/activities/mongoAuthRepo/tests/getClient.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as assert from 'assert';
22
import btoa from 'btoa';
33
import NoModel from 'jscommons/dist/errors/NoModel';
44
import assertError from 'jscommons/dist/tests/utils/assertError';
5-
import { ObjectID } from 'mongodb';
5+
import { ObjectId } from 'mongodb';
66
import connectToMongoDb from '../../../../utils/connectToMongoDb';
77
import ExpiredClientError from '../../errors/ExpiredClientError';
88
import UntrustedClientError from '../../errors/UntrustedClientError';
@@ -13,7 +13,7 @@ const TEST_BASIC_SECRET = 'abc';
1313
const TEST_TOKEN = `Basic ${btoa(`${TEST_BASIC_KEY}:${TEST_BASIC_SECRET}`)}`;
1414
const TEST_ACCESS_TOKEN = '11112222-3333-4444-5555-666677778888';
1515
const TEST_CLIENT = {
16-
_id: new ObjectID('5988f0f00000000000000123'),
16+
_id: new ObjectId('5988f0f00000000000000123'),
1717
api: {
1818
basic_key: TEST_BASIC_KEY,
1919
basic_secret: TEST_BASIC_SECRET,
@@ -22,27 +22,27 @@ const TEST_CLIENT = {
2222
mbox: 'mailto:[email protected]',
2323
objectType: 'Agent',
2424
}),
25-
lrs_id: new ObjectID('5988f0f00000000000000001'),
26-
organisation: new ObjectID('5988f0f00000000000000000'),
25+
lrs_id: new ObjectId('5988f0f00000000000000001'),
26+
organisation: new ObjectId('5988f0f00000000000000000'),
2727
};
2828
const TEST_ORG = {
29-
_id: new ObjectID('5988f0f00000000000000000'),
29+
_id: new ObjectId('5988f0f00000000000000000'),
3030
createdAt: new Date('2017-10-25T14:39:44.962Z'),
3131
name: 'Test Org',
3232
updatedAt: new Date('2017-10-25T14:39:58.376Z'),
3333
};
3434
const TEST_STORE = {
35-
_id: new ObjectID('5988f0f00000000000000001'),
35+
_id: new ObjectId('5988f0f00000000000000001'),
3636
createdAt: new Date('2017-10-25T14:39:44.962Z'),
3737
description: 'Test LRS Description',
38-
organisation: new ObjectID('5988f0f00000000000000000'),
38+
organisation: new ObjectId('5988f0f00000000000000000'),
3939
statementCount: 0,
4040
title: 'Test LRS',
4141
updatedAt: new Date('2017-10-25T14:39:58.376Z'),
4242
};
4343
const TEST_OAUTH_TOKEN = {
44-
_id: new ObjectID('5988f0f00000000000000002'),
45-
clientId: new ObjectID('5988f0f00000000000000123'),
44+
_id: new ObjectId('5988f0f00000000000000002'),
45+
clientId: new ObjectId('5988f0f00000000000000123'),
4646
accessToken: TEST_ACCESS_TOKEN,
4747
createdAt: new Date('2017-10-25T14:39:44.962Z'),
4848
expireAt: new Date('2017-10-25T15:39:44.962Z'),

src/apps/activities/mongoModelsRepo/deleteProfile.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import NoModel from 'jscommons/dist/errors/NoModel';
2-
import { ObjectID } from 'mongodb';
2+
import { ObjectId } from 'mongodb';
33
import IfMatch from '../errors/IfMatch';
44
import DeleteProfileOptions from '../repoFactory/options/DeleteProfileOptions';
55
import DeleteProfileResult from '../repoFactory/results/DeleteProfileResult';
@@ -15,8 +15,8 @@ export default (config: Config) => {
1515

1616
const profileFilter = {
1717
activityId: opts.activityId,
18-
lrs: new ObjectID(opts.client.lrs_id),
19-
organisation: new ObjectID(opts.client.organisation),
18+
lrs: new ObjectId(opts.client.lrs_id),
19+
organisation: new ObjectId(opts.client.organisation),
2020
profileId: opts.profileId,
2121
};
2222

@@ -33,12 +33,12 @@ export default (config: Config) => {
3333

3434
// Determines if the identifier was deleted.
3535
// Docs: https://docs.mongodb.com/manual/reference/command/getLastError/#getLastError.n
36-
const matchedDocuments = opResult.lastErrorObject.n as number;
36+
const matchedDocuments = opResult.lastErrorObject?.n as number;
3737
const wasDeleted = matchedDocuments === 1;
3838

3939
// Returns the result of the deletion if the document was deleted.
4040
if (wasDeleted) {
41-
const deletedDoc = opResult.value;
41+
const deletedDoc = opResult.value as any;
4242
return {
4343
contentType: deletedDoc.contentType,
4444
extension: deletedDoc.extension,

src/apps/activities/mongoModelsRepo/getProfile.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import NoModel from 'jscommons/dist/errors/NoModel';
22
import { defaultTo } from 'lodash';
3-
import { ObjectID } from 'mongodb';
3+
import { ObjectId } from 'mongodb';
44
import GetProfileOptions from '../repoFactory/options/GetProfileOptions';
55
import GetProfileResult from '../repoFactory/results/GetProfileResult';
66
import Config from './Config';
@@ -12,8 +12,8 @@ export default (config: Config) => {
1212

1313
const filter = {
1414
activityId: opts.activityId,
15-
lrs: new ObjectID(opts.client.lrs_id),
16-
organisation: new ObjectID(opts.client.organisation),
15+
lrs: new ObjectId(opts.client.lrs_id),
16+
organisation: new ObjectId(opts.client.organisation),
1717
profileId: opts.profileId,
1818
};
1919

src/apps/activities/mongoModelsRepo/getProfiles.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ObjectID } from 'mongodb';
1+
import { ObjectId } from 'mongodb';
22
import GetProfilesOptions from '../repoFactory/options/GetProfilesOptions';
33
import GetProfilesResult from '../repoFactory/results/GetProfilesResult';
44
import Config from './Config';
@@ -11,8 +11,8 @@ export default (config: Config) => {
1111
const sinceFilter = opts.since !== undefined ? { updatedAt: { $gt: opts.since } } : {};
1212
const filter = {
1313
activityId: opts.activityId,
14-
lrs: new ObjectID(opts.client.lrs_id),
15-
organisation: new ObjectID(opts.client.organisation),
14+
lrs: new ObjectId(opts.client.lrs_id),
15+
organisation: new ObjectId(opts.client.organisation),
1616
...sinceFilter,
1717
};
1818

src/apps/activities/mongoModelsRepo/hasProfile.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ObjectID } from 'mongodb';
1+
import { ObjectId } from 'mongodb';
22
import HasProfileOptions from '../repoFactory/options/HasProfileOptions';
33
import HasProfileResult from '../repoFactory/results/HasProfileResult';
44
import Config from './Config';
@@ -10,14 +10,14 @@ export default (config: Config) => {
1010

1111
const filter = {
1212
activityId: opts.activityId,
13-
lrs: new ObjectID(opts.client.lrs_id),
14-
organisation: new ObjectID(opts.client.organisation),
13+
lrs: new ObjectId(opts.client.lrs_id),
14+
organisation: new ObjectId(opts.client.organisation),
1515
profileId: opts.profileId,
1616
};
1717

1818
// Docs: http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#findOne
1919
const document = await collection.findOne(filter, {
20-
fields: {
20+
projection: {
2121
_id: 0,
2222
},
2323
});

src/apps/activities/mongoModelsRepo/overwriteProfile.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isPlainObject } from 'lodash';
2-
import { ObjectID } from 'mongodb';
2+
import { ObjectId, ReturnDocument } from 'mongodb';
33
import IfMatch from '../errors/IfMatch';
44
import IfNoneMatch from '../errors/IfNoneMatch';
55
import MaxEtags from '../errors/MaxEtags';
@@ -23,8 +23,8 @@ export default (config: Config) => {
2323

2424
const profileFilter = {
2525
activityId: opts.activityId,
26-
lrs: new ObjectID(opts.client.lrs_id),
27-
organisation: new ObjectID(opts.client.organisation),
26+
lrs: new ObjectId(opts.client.lrs_id),
27+
organisation: new ObjectId(opts.client.organisation),
2828
profileId: opts.profileId,
2929
};
3030

@@ -56,39 +56,36 @@ export default (config: Config) => {
5656
$set: update,
5757
},
5858
{
59-
returnOriginal: false, // Ensures the updated document is returned.
59+
returnDocument: ReturnDocument.AFTER, // Ensures the updated document is returned.
6060
upsert: false, // Does not create the profile when it doesn't exist.
6161
},
6262
);
6363

6464
// Determines if the Profile was updated.
6565
// Docs: https://docs.mongodb.com/manual/reference/command/getLastError/#getLastError.n
66-
const updatedDocuments = updateOpResult.lastErrorObject.n as number;
66+
const updatedDocuments = updateOpResult.lastErrorObject?.n as number;
6767
if (updatedDocuments === 1) {
68+
const opResult = await collection.findOne({ _id: updateOpResult.value?._id });
69+
6870
return {
69-
extension: updateOpResult.value.extension,
70-
id: updateOpResult.value._id.toString(),
71+
extension: opResult?.extension,
72+
id: opResult?._id.toString() as string,
7173
};
7274
}
7375
}
7476

75-
// Creates the profile if it doesn't already exist.
76-
// Docs: http://mongodb.github.io/node-mongodb-native/2.2/api/Collection.html#findOneAndUpdate
77-
// Docs: http://bit.ly/findAndModifyWriteOpResult
7877
const createOpResult = await collection.findOneAndUpdate(
7978
profileFilter,
8079
{
8180
$setOnInsert: update,
8281
},
8382
{
84-
returnOriginal: false, // Ensures the updated document is returned.
85-
upsert: true, // Creates the profile when it's not found.
83+
returnDocument: ReturnDocument.AFTER,
84+
upsert: true,
8685
},
8786
);
8887

89-
// Determines if the Profile was created or found.
90-
// Docs: https://docs.mongodb.com/manual/reference/command/getLastError/#getLastError.n
91-
const wasCreated = createOpResult.lastErrorObject.upserted !== undefined;
88+
const wasCreated = !createOpResult.lastErrorObject?.updatedExisting;
9289

9390
// Throws the IfMatch error when the profile already exists.
9491
// This is because there must have been an ETag mismatch in the previous update.
@@ -100,9 +97,13 @@ export default (config: Config) => {
10097
throw new IfNoneMatch();
10198
}
10299

100+
const id = wasCreated ? createOpResult.lastErrorObject?.upserted : createOpResult.value?._id;
101+
102+
const opResult = await collection.findOne({ _id: id });
103+
103104
return {
104-
extension: createOpResult.value.extension,
105-
id: createOpResult.value._id.toString(),
105+
extension: opResult?.extension,
106+
id: opResult?._id.toString() as string,
106107
};
107108
};
108109
};

src/apps/activities/mongoModelsRepo/patchProfile.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { mapKeys } from 'lodash';
2-
import { ObjectID } from 'mongodb';
2+
import { ObjectId, ReturnDocument } from 'mongodb';
33
import IfMatch from '../errors/IfMatch';
44
import IfNoneMatch from '../errors/IfNoneMatch';
55
import MaxEtags from '../errors/MaxEtags';
@@ -30,8 +30,8 @@ export default (config: Config) => {
3030

3131
const profileFilter = {
3232
activityId: opts.activityId,
33-
lrs: new ObjectID(opts.client.lrs_id),
34-
organisation: new ObjectID(opts.client.organisation),
33+
lrs: new ObjectId(opts.client.lrs_id),
34+
organisation: new ObjectId(opts.client.organisation),
3535
profileId: opts.profileId,
3636
};
3737

@@ -71,14 +71,14 @@ export default (config: Config) => {
7171
},
7272
},
7373
{
74-
returnOriginal: false, // Ensures the updated document is returned.
74+
returnDocument: ReturnDocument.AFTER, // Ensures the updated document is returned.
7575
upsert: false, // Does not create the profile when it doesn't exist.
7676
},
7777
);
7878

7979
// Determines if the Profile was updated.
8080
// Docs: https://docs.mongodb.com/manual/reference/command/getLastError/#getLastError.n
81-
const updatedDocuments = updateOpResult.lastErrorObject.n as number;
81+
const updatedDocuments = updateOpResult.lastErrorObject?.n as number;
8282
if (updatedDocuments === 1) {
8383
return;
8484
}
@@ -96,20 +96,20 @@ export default (config: Config) => {
9696
},
9797
},
9898
{
99-
returnOriginal: false, // Ensures the updated document is returned.
99+
returnDocument: ReturnDocument.AFTER, // Ensures the updated document is returned.
100100
upsert: true, // Creates the profile when it's not found.
101101
},
102102
);
103103

104104
// Determines if the Profile was created or found.
105105
// Docs: https://docs.mongodb.com/manual/reference/command/getLastError/#getLastError.n
106-
const wasCreated = createOpResult.lastErrorObject.upserted !== undefined;
106+
const wasCreated = createOpResult.lastErrorObject?.upserted !== undefined;
107107

108108
// When the profile is found at the create stage but not the update stage,
109109
// And the ifNoneMatch option was not provided.
110110
// Then the exsting profile either has the wrong content or didn't match the ifMatch option.
111111
if (!wasCreated && !checkIfNoneMatch) {
112-
if (checkIfMatch && createOpResult.value.etag !== opts.ifMatch) {
112+
if (checkIfMatch && createOpResult.value?.etag !== opts.ifMatch) {
113113
throw new IfMatch();
114114
}
115115
throw new NonJsonObject();

src/apps/agents/mongoAuthRepo/tests/getClient.test.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as assert from 'assert';
22
import btoa from 'btoa';
33
import NoModel from 'jscommons/dist/errors/NoModel';
44
import assertError from 'jscommons/dist/tests/utils/assertError';
5-
import { ObjectID } from 'mongodb';
5+
import { ObjectId } from 'mongodb';
66
import connectToMongoDb from '../../../../utils/connectToMongoDb';
77
import ExpiredClientError from '../../errors/ExpiredClientError';
88
import UntrustedClientError from '../../errors/UntrustedClientError';
@@ -13,7 +13,7 @@ const TEST_BASIC_SECRET = 'abc';
1313
const TEST_TOKEN = `Basic ${btoa(`${TEST_BASIC_KEY}:${TEST_BASIC_SECRET}`)}`;
1414
const TEST_ACCESS_TOKEN = '11112222-3333-4444-5555-666677778888';
1515
const TEST_CLIENT = {
16-
_id: new ObjectID('5988f0f00000000000000123'),
16+
_id: new ObjectId('5988f0f00000000000000123'),
1717
api: {
1818
basic_key: TEST_BASIC_KEY,
1919
basic_secret: TEST_BASIC_SECRET,
@@ -22,27 +22,27 @@ const TEST_CLIENT = {
2222
mbox: 'mailto:[email protected]',
2323
objectType: 'Agent',
2424
}),
25-
lrs_id: new ObjectID('5988f0f00000000000000001'),
26-
organisation: new ObjectID('5988f0f00000000000000000'),
25+
lrs_id: new ObjectId('5988f0f00000000000000001'),
26+
organisation: new ObjectId('5988f0f00000000000000000'),
2727
};
2828
const TEST_ORG = {
29-
_id: new ObjectID('5988f0f00000000000000000'),
29+
_id: new ObjectId('5988f0f00000000000000000'),
3030
createdAt: new Date('2017-10-25T14:39:44.962Z'),
3131
name: 'Test Org',
3232
updatedAt: new Date('2017-10-25T14:39:58.376Z'),
3333
};
3434
const TEST_STORE = {
35-
_id: new ObjectID('5988f0f00000000000000001'),
35+
_id: new ObjectId('5988f0f00000000000000001'),
3636
createdAt: new Date('2017-10-25T14:39:44.962Z'),
3737
description: 'Test LRS Description',
38-
organisation: new ObjectID('5988f0f00000000000000000'),
38+
organisation: new ObjectId('5988f0f00000000000000000'),
3939
statementCount: 0,
4040
title: 'Test LRS',
4141
updatedAt: new Date('2017-10-25T14:39:58.376Z'),
4242
};
4343
const TEST_OAUTH_TOKEN = {
44-
_id: new ObjectID('5988f0f00000000000000002'),
45-
clientId: new ObjectID('5988f0f00000000000000123'),
44+
_id: new ObjectId('5988f0f00000000000000002'),
45+
clientId: new ObjectId('5988f0f00000000000000123'),
4646
accessToken: TEST_ACCESS_TOKEN,
4747
createdAt: new Date('2017-10-25T14:39:44.962Z'),
4848
expireAt: new Date('2017-10-25T15:39:44.962Z'),

src/apps/agents/mongoModelsRepo/deleteProfile.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,16 @@ export default (config: Config) => {
2626
);
2727

2828
// Determines if the identifier was deleted.
29-
const matchedDocuments = opResult.lastErrorObject.n as number;
29+
const matchedDocuments = opResult.lastErrorObject?.n as number;
3030
const wasDeleted = matchedDocuments === 1;
3131

3232
// Returns the result of the deletion if the document was deleted.
3333
if (wasDeleted) {
3434
const deletedDoc = opResult.value;
3535
return {
36-
contentType: deletedDoc.contentType,
37-
extension: deletedDoc.extension,
38-
id: deletedDoc._id.toString(),
36+
contentType: deletedDoc?.contentType,
37+
extension: deletedDoc?.extension,
38+
id: deletedDoc?._id.toString() as string,
3939
};
4040
}
4141

0 commit comments

Comments
 (0)