adapt-authoring-api 1.0.0 → 1.1.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/workflows/standardjs.yml +13 -0
- package/lib/AbstractApiModule.js +8 -6
- package/lib/DataCache.js +3 -4
- package/lib/typedefs.js +1 -1
- package/package.json +1 -2
- package/.eslintignore +0 -1
- package/.eslintrc +0 -14
- package/tests/abstractApiModule.spec.js +0 -84
- package/tests/abstractApiUtils.spec.js +0 -49
- package/tests/data/testApiModule.js +0 -50
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
name: Standard.js formatting check
|
|
2
|
+
on: push
|
|
3
|
+
jobs:
|
|
4
|
+
default:
|
|
5
|
+
runs-on: ubuntu-latest
|
|
6
|
+
steps:
|
|
7
|
+
- uses: actions/checkout@master
|
|
8
|
+
- uses: actions/setup-node@master
|
|
9
|
+
with:
|
|
10
|
+
node-version: 'lts/*'
|
|
11
|
+
cache: 'npm'
|
|
12
|
+
- run: npm ci
|
|
13
|
+
- run: npx standard
|
package/lib/AbstractApiModule.js
CHANGED
|
@@ -37,6 +37,7 @@ class AbstractApiModule extends AbstractModule {
|
|
|
37
37
|
}
|
|
38
38
|
]
|
|
39
39
|
}
|
|
40
|
+
|
|
40
41
|
/**
|
|
41
42
|
* Returns the 'OK' status code to match the HTTP method
|
|
42
43
|
* @param {String} httpMethod
|
|
@@ -355,8 +356,8 @@ class AbstractApiModule extends AbstractModule {
|
|
|
355
356
|
let data
|
|
356
357
|
try {
|
|
357
358
|
await this.requestHook.invoke(req)
|
|
358
|
-
const preCheck =
|
|
359
|
-
const postCheck =
|
|
359
|
+
const preCheck = method !== 'get' && method !== 'post'
|
|
360
|
+
const postCheck = method === 'get'
|
|
360
361
|
if (preCheck) {
|
|
361
362
|
await this.checkAccess(req, req.apiData.query)
|
|
362
363
|
}
|
|
@@ -374,7 +375,7 @@ class AbstractApiModule extends AbstractModule {
|
|
|
374
375
|
}
|
|
375
376
|
data = data[0]
|
|
376
377
|
}
|
|
377
|
-
if (method !== 'get') {
|
|
378
|
+
if (method !== 'get') {
|
|
378
379
|
const resource = Array.isArray(data) ? req.apiData.query : data._id.toString()
|
|
379
380
|
this.log('debug', `API_${func.name.toUpperCase()}`, resource, 'by', req.auth.user._id.toString())
|
|
380
381
|
}
|
|
@@ -413,6 +414,8 @@ class AbstractApiModule extends AbstractModule {
|
|
|
413
414
|
// remove any valid query keys from the options
|
|
414
415
|
Object.keys(req.apiData.query).forEach(key => delete opts[key])
|
|
415
416
|
|
|
417
|
+
await this.requestHook.invoke(req)
|
|
418
|
+
|
|
416
419
|
await this.setUpPagination(req, res, mongoOpts)
|
|
417
420
|
|
|
418
421
|
let results = await this.find(req.apiData.query, opts, mongoOpts)
|
|
@@ -559,9 +562,8 @@ class AbstractApiModule extends AbstractModule {
|
|
|
559
562
|
*/
|
|
560
563
|
async find (query, options = {}, mongoOptions = {}) {
|
|
561
564
|
this.setDefaultOptions(options)
|
|
562
|
-
const mongodb = await this.app.waitForModule('mongodb')
|
|
563
565
|
const q = options.validate ? await this.parseQuery(options.schemaName, query, options, mongoOptions) : query
|
|
564
|
-
return
|
|
566
|
+
return this.cache.get(q, options, mongoOptions)
|
|
565
567
|
}
|
|
566
568
|
|
|
567
569
|
/**
|
|
@@ -594,7 +596,7 @@ class AbstractApiModule extends AbstractModule {
|
|
|
594
596
|
if (options.invokePostHook !== false) await this.postUpdateHook.invoke(originalDoc, results)
|
|
595
597
|
return results
|
|
596
598
|
}
|
|
597
|
-
|
|
599
|
+
|
|
598
600
|
/**
|
|
599
601
|
* Updates existing documents in the DB
|
|
600
602
|
* @param {Object} query Attributes to use to filter DB documents
|
package/lib/DataCache.js
CHANGED
|
@@ -6,7 +6,7 @@ import { App } from 'adapt-authoring-core'
|
|
|
6
6
|
class DataCache {
|
|
7
7
|
/** @override */
|
|
8
8
|
constructor ({ enable, lifespan }) {
|
|
9
|
-
this.isEnabled = enable
|
|
9
|
+
this.isEnabled = enable === true
|
|
10
10
|
this.lifespan = lifespan ?? App.instance.config.get('adapt-authoring-api.defaultCacheLifespan')
|
|
11
11
|
this.cache = {}
|
|
12
12
|
}
|
|
@@ -21,7 +21,7 @@ class DataCache {
|
|
|
21
21
|
async get (query, options, mongoOptions) {
|
|
22
22
|
const key = JSON.stringify(query) + JSON.stringify(options) + JSON.stringify(mongoOptions)
|
|
23
23
|
this.prune()
|
|
24
|
-
if (this.cache[key]) {
|
|
24
|
+
if (this.isEnabled && this.cache[key]) {
|
|
25
25
|
return this.cache[key].data
|
|
26
26
|
}
|
|
27
27
|
const mongodb = await App.instance.waitForModule('mongodb')
|
|
@@ -35,8 +35,7 @@ class DataCache {
|
|
|
35
35
|
*/
|
|
36
36
|
prune () {
|
|
37
37
|
Object.keys(this.cache).forEach(k => {
|
|
38
|
-
|
|
39
|
-
if (Date.now() > (cache.timestamp + this.lifespan)) {
|
|
38
|
+
if (Date.now() > (this.cache[k].timestamp + this.lifespan)) {
|
|
40
39
|
delete this.cache[k]
|
|
41
40
|
}
|
|
42
41
|
})
|
package/lib/typedefs.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adapt-authoring-api",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Abstract module for creating APIs",
|
|
5
5
|
"homepage": "https://github.com/adapt-security/adapt-authoring-api",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"adapt-authoring-mongodb": "github:adapt-security/adapt-authoring-mongodb"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"eslint": "^9.14.0",
|
|
21
20
|
"standard": "^17.1.0",
|
|
22
21
|
"@semantic-release/commit-analyzer": "^9.0.2",
|
|
23
22
|
"@semantic-release/git": "^10.0.1",
|
package/.eslintignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
node_modules
|
package/.eslintrc
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
const { App } = require('adapt-authoring-core');
|
|
2
|
-
const should = require('should');
|
|
3
|
-
const TestApiModule = require('./data/testApiModule');
|
|
4
|
-
|
|
5
|
-
describe('Abstract API module', function() {
|
|
6
|
-
before(function(done) {
|
|
7
|
-
const loadModule = (mod, done) => {
|
|
8
|
-
const m = this.app.getModule(mod);
|
|
9
|
-
m.preload (this.app, () => m.boot(this.app, done, done), done);
|
|
10
|
-
};
|
|
11
|
-
this.app = App.instance;
|
|
12
|
-
|
|
13
|
-
loadModule('server', () => loadModule('mongodb', done));
|
|
14
|
-
|
|
15
|
-
this.tmi = new TestApiModule(this.app, {});
|
|
16
|
-
this.tmi.router.should.not.be.undefined();
|
|
17
|
-
});
|
|
18
|
-
describe('#requestHandler()', function() {
|
|
19
|
-
it('should customise the request object', function() {
|
|
20
|
-
const req = { method: 'GET' };
|
|
21
|
-
TestApiModule.requestHandler()(req, {}, () => {});
|
|
22
|
-
should.exist(req.type);
|
|
23
|
-
should.exist(req.dsquery);
|
|
24
|
-
});
|
|
25
|
-
it('should correctly map HTTP methods to MongoDBModule functions', function() {
|
|
26
|
-
const m1 = this.app.getModule('mongodb');
|
|
27
|
-
const m2 = {
|
|
28
|
-
retrieve: () => {
|
|
29
|
-
return new Promise((resolve, reject) => { correctlyMapped = true; })
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
let correctlyMapped = false;
|
|
33
|
-
this.app.dependencyloader.modules['adapt-authoring-mongodb'] = m2;
|
|
34
|
-
TestApiModule.requestHandler()({ method: 'GET' }, {}, () => {});
|
|
35
|
-
this.app.dependencyloader.modules['adapt-authoring-mongodb'] = m1;
|
|
36
|
-
correctlyMapped.should.be.true();
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe('#preload()', function() {
|
|
40
|
-
it('should create a child Router with the correct route', function(done) {
|
|
41
|
-
this.tmi.preload(this.app, () => {
|
|
42
|
-
this.tmi.router.constructor.name.should.equal('Router');
|
|
43
|
-
done();
|
|
44
|
-
}, done);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
describe('#initSchemas()', function() {
|
|
48
|
-
it('should add specified schemas to the DB', function(done) {
|
|
49
|
-
this.tmi.boot(this.app, () => {
|
|
50
|
-
should.exist(this.app.getModule('mongodb').connection.models.test);
|
|
51
|
-
done();
|
|
52
|
-
}, done);
|
|
53
|
-
});
|
|
54
|
-
it('should ignore badly configured data', function() {
|
|
55
|
-
const mongoModels = this.app.getModule('mongodb').connection.models;
|
|
56
|
-
Object.keys(mongoModels).length.should.equal(1);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
describe('#initMiddleware()', function() {
|
|
60
|
-
it('should add middleware to the API router', function() {
|
|
61
|
-
const middleware = this.tmi.router.middleware;
|
|
62
|
-
middleware.length.should.equal(1);
|
|
63
|
-
middleware[0].name.should.equal('testMiddleware');
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
describe('#initRoutes()', function() {
|
|
67
|
-
it('should add routes defined as an object', function() {
|
|
68
|
-
const routes = this.tmi.router.routes.map(r => r.route);
|
|
69
|
-
routes.should.containEql('/objectroute');
|
|
70
|
-
});
|
|
71
|
-
it('should add routes defined as an array', function() {
|
|
72
|
-
const routes = this.tmi.router.routes.map(r => r.route);
|
|
73
|
-
routes.should.containEql('/arrayroute');
|
|
74
|
-
});
|
|
75
|
-
it('should set custom permissions scopes if specified', function() {
|
|
76
|
-
const postScopes = this.app.auth.routes.secure['/api/arrayroute'].post;
|
|
77
|
-
postScopes.should.containEql('testscope');
|
|
78
|
-
});
|
|
79
|
-
it('should set generic permissions scopes if not specified', function() {
|
|
80
|
-
const scopes = this.app.auth.routes.secure['/api/objectroute'];
|
|
81
|
-
scopes.should.not.be.undefined();
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
const should = require('should');
|
|
2
|
-
|
|
3
|
-
describe('Abstract API utilities', function() {
|
|
4
|
-
describe('#callDbFunction()', function() {
|
|
5
|
-
it('should fail if request parameter doesn\'t specify a type', function() {
|
|
6
|
-
false.should.be.true();
|
|
7
|
-
});
|
|
8
|
-
it('should fail if attempting to call an unknown DB function', function() {
|
|
9
|
-
false.should.be.true();
|
|
10
|
-
});
|
|
11
|
-
it('should return data in response', function() {
|
|
12
|
-
false.should.be.true();
|
|
13
|
-
});
|
|
14
|
-
it('should set response HTTP status', function() {
|
|
15
|
-
false.should.be.true();
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
describe('#httpMethodToAction()', function() {
|
|
19
|
-
it('should return a string', function() {
|
|
20
|
-
false.should.be.true();
|
|
21
|
-
});
|
|
22
|
-
it('should return action string for known action', function() {
|
|
23
|
-
false.should.be.true();
|
|
24
|
-
});
|
|
25
|
-
it('should return empty string for unknown action', function() {
|
|
26
|
-
false.should.be.true();
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
describe('#validateSchemaDef()', function() {
|
|
30
|
-
it('should fail if def isn\'t an object', function() {
|
|
31
|
-
false.should.be.true();
|
|
32
|
-
});
|
|
33
|
-
it('should fail if def has no name', function() {
|
|
34
|
-
false.should.be.true();
|
|
35
|
-
});
|
|
36
|
-
it('should fail if def has no model', function() {
|
|
37
|
-
false.should.be.true();
|
|
38
|
-
});
|
|
39
|
-
it('should fail if schemas isn\'t an array', function() {
|
|
40
|
-
false.should.be.true();
|
|
41
|
-
});
|
|
42
|
-
it('should fail if def has no routes', function() {
|
|
43
|
-
false.should.be.true();
|
|
44
|
-
});
|
|
45
|
-
it('should validate routes', function() {
|
|
46
|
-
false.should.be.true();
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
const AbstractAPIModule = require('../../lib/AbstractApiModule');
|
|
2
|
-
|
|
3
|
-
const TestSchema = {
|
|
4
|
-
name: 'test',
|
|
5
|
-
definition: {
|
|
6
|
-
isTest: {
|
|
7
|
-
type: "boolean", default: true
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
class TestApiModule extends AbstractAPIModule {
|
|
13
|
-
static get def() {
|
|
14
|
-
return {
|
|
15
|
-
name: 'test',
|
|
16
|
-
model: 'test',
|
|
17
|
-
schemas: [
|
|
18
|
-
TestSchema,
|
|
19
|
-
{ name: 't2' },
|
|
20
|
-
{ definition: {} }
|
|
21
|
-
],
|
|
22
|
-
middleware: [testMiddleware],
|
|
23
|
-
routes: [
|
|
24
|
-
{
|
|
25
|
-
route: '/arrayroute',
|
|
26
|
-
handlers: ['post','get','put','delete'],
|
|
27
|
-
scopes: { post: 'testscope' }
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
route: '/objectroute',
|
|
31
|
-
handlers: {
|
|
32
|
-
post: testRouteHandler,
|
|
33
|
-
get: testRouteHandler,
|
|
34
|
-
put: testRouteHandler,
|
|
35
|
-
delete: testRouteHandler
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
]
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function testMiddleware(req, res, next) {
|
|
44
|
-
console.log('Test middleware called');
|
|
45
|
-
}
|
|
46
|
-
function testRouteHandler(req, res, next) {
|
|
47
|
-
console.log('Test handler called');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
module.exports = TestApiModule;
|