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.
@@ -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
@@ -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 = !req.auth.isSuper && method !== 'get' && method !== 'post'
359
- const postCheck = !req.auth.isSuper && method === 'get'
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 mongodb.find(options.collectionName, q, mongoOptions)
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 !== false
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
- const cache = this.cache[k]
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
@@ -64,4 +64,4 @@
64
64
  * @typedef {Object} DeleteOptions
65
65
  * @property {String} collectionName DB collection to remove document from
66
66
  * @property {String} invokePostHook Whether the function should invoke the 'post' action hook on success
67
- */
67
+ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adapt-authoring-api",
3
- "version": "1.0.0",
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,14 +0,0 @@
1
- {
2
- "env": {
3
- "browser": false,
4
- "node": true,
5
- "commonjs": false,
6
- "es2020": true
7
- },
8
- "extends": [
9
- "standard"
10
- ],
11
- "parserOptions": {
12
- "ecmaVersion": 2020
13
- }
14
- }
@@ -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;