adapt-authoring-api 0.0.1 → 1.0.1
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/ISSUE_TEMPLATE/bug_report.yml +55 -55
- package/.github/ISSUE_TEMPLATE/feature_request.yml +22 -22
- package/.github/dependabot.yml +11 -11
- package/.github/pull_request_template.md +25 -25
- package/.github/workflows/labelled_prs.yml +16 -16
- package/.github/workflows/new.yml +19 -19
- package/.github/workflows/releases.yml +25 -0
- package/.github/workflows/standardjs.yml +13 -0
- package/adapt-authoring.json +9 -9
- package/conf/config.schema.json +22 -22
- package/docs/writing-an-api.md +135 -135
- package/errors/errors.json +31 -31
- package/index.js +7 -7
- package/lib/AbstractApiModule.js +670 -668
- package/lib/AbstractApiUtils.js +145 -145
- package/lib/DataCache.js +46 -46
- package/lib/typedefs.js +66 -66
- package/package.json +57 -23
- package/tests/abstractApiModule.spec.js +84 -84
- package/tests/abstractApiUtils.spec.js +49 -49
- package/tests/data/testApiModule.js +50 -50
- package/.eslintignore +0 -1
- package/.eslintrc +0 -14
package/docs/writing-an-api.md
CHANGED
|
@@ -1,135 +1,135 @@
|
|
|
1
|
-
# Writing an API
|
|
2
|
-
|
|
3
|
-
_**Note:** before using this functionality, it is worth having an understanding of how to write basic custom modules first. See [this page](`adapt-authoring-api` module) for more._
|
|
4
|
-
|
|
5
|
-
The `adapt-authoring-api` module makes defining custom APIs simple by providing abstract classes and utilities you can use in your own modules to replace the common boilerplate code required when writing an API.
|
|
6
|
-
|
|
7
|
-
By extending the [AbstractAPIModule](../class/adapt_authoring_restructure/adapt-authoring-api/lib/module.js~AbstractApiModule.html) class, you get the following as standard:
|
|
8
|
-
- Boiler-plate code for defining router and endpoints
|
|
9
|
-
- Default handlers for incoming HTTP requests (with support for querying)
|
|
10
|
-
- Support for custom middleware
|
|
11
|
-
- Auto-loading of database schemas
|
|
12
|
-
- Automated (and overridable) database interaction
|
|
13
|
-
- Default permissions
|
|
14
|
-
|
|
15
|
-
## Defining your API
|
|
16
|
-
|
|
17
|
-
To define an API, all you need to do is override the `setValues` function, making sure to change the values as appropriate.
|
|
18
|
-
|
|
19
|
-
See the below table for all values:
|
|
20
|
-
|
|
21
|
-
| Attribute | Type | Description | Optional |
|
|
22
|
-
| --------- | ---- | ----------- | -------- |
|
|
23
|
-
| `root` | `String` | This value will be used as the route for any URLs (e.g. `/api/mymodule`). For APIs which deal with collections of data, the general rule is to use the plural form (ie. `/api/items`). | `false` |
|
|
24
|
-
| `router` | `Router` | The Router instance used for HTTP requests. If not defined, a new router will be created using the `root` value. | `true` |
|
|
25
|
-
| `routes` | `Array<ApiRoute>` | Definitions for any routes to be added to the API router. | `false` |
|
|
26
|
-
| `collectionName` | `String` | Default DB collection to store data to (can be overridden by individual handlers). | `false` |
|
|
27
|
-
|
|
28
|
-
### Defining routes
|
|
29
|
-
|
|
30
|
-
Before your API can handle HTTP requests, you must define which routes the router should respond to. If your module extends AbstractApiModule, this is simply a case of setting the [`routes`](../class/node_modules/adapt-authoring-api/lib/abstractApiModule.js~AbstractApiModule.html#instance-member-routes) instance variable:
|
|
31
|
-
|
|
32
|
-
```js
|
|
33
|
-
this.routes = [
|
|
34
|
-
// route config here
|
|
35
|
-
]
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
#### General tips
|
|
39
|
-
The Adapt server module maps to Express functionality as closely as possible, and as such adopts the same middleware/handler concepts (with a few minor changes). It is therefore very useful to have some understanding of how the Express stack works, particularly with regards to the execution order of middleware and handlers. For more details on route handling, see the [official Express documentation](https://expressjs.com/en/guide/routing.html).
|
|
40
|
-
|
|
41
|
-
Here are a few useful notes/tips:
|
|
42
|
-
|
|
43
|
-
- The `route` attribute of each route definition is very powerful, and handles params/queries/etc. in the same way as Express (see [the Express docs](https://expressjs.com/en/guide/routing.html) for more).
|
|
44
|
-
- Use the API's middleware to perform any tasks which are common to all routes, such as checking and formatting the request data
|
|
45
|
-
- You only need to specify route handlers for the routes/HTTP methods you want to enable, access will be blocked to any route/HTTP method combinations you haven't defined
|
|
46
|
-
- You can run route-specific middleware by adding it as a handler to the route config (see example 2. below)
|
|
47
|
-
|
|
48
|
-
#### Using the default route configuration
|
|
49
|
-
Instead of defining each route yourself, the AbstractApiModule class also gives you a set of default routes which you can use if you wish:
|
|
50
|
-
```js
|
|
51
|
-
[
|
|
52
|
-
// POST, no params
|
|
53
|
-
{
|
|
54
|
-
route: '/',
|
|
55
|
-
modifying: true,
|
|
56
|
-
handlers: {
|
|
57
|
-
post: this.requestHandler()
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
// GET, optional _id param
|
|
61
|
-
{
|
|
62
|
-
route: '/:_id?',
|
|
63
|
-
handlers: {
|
|
64
|
-
get: this.requestHandler()
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
// PUT/DELETE, mandatory _id param
|
|
68
|
-
{
|
|
69
|
-
route: '/:_id',
|
|
70
|
-
modifying: true,
|
|
71
|
-
handlers: {
|
|
72
|
-
put: this.requestHandler(),
|
|
73
|
-
delete: this.requestHandler()
|
|
74
|
-
}
|
|
75
|
-
},
|
|
76
|
-
// POST custom query handler
|
|
77
|
-
{
|
|
78
|
-
route: '/query',
|
|
79
|
-
validate: false,
|
|
80
|
-
handlers: {
|
|
81
|
-
post: this.queryHandler()
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
];
|
|
85
|
-
```
|
|
86
|
-
To use the above configuration, you simply need to call the `useDefaultRouteConfig` function:
|
|
87
|
-
```js
|
|
88
|
-
async setValues() {
|
|
89
|
-
this.useDefaultRouteConfig();
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### Example configurations
|
|
94
|
-
```js
|
|
95
|
-
/**
|
|
96
|
-
* Basic configuration
|
|
97
|
-
*/
|
|
98
|
-
async setValues() {
|
|
99
|
-
const server = await this.app.waitForModule('server');
|
|
100
|
-
this.root = 'myapi';
|
|
101
|
-
this.collectionName = 'mycollection';
|
|
102
|
-
this.router = server.api.createChildRouter('myapi'); // optional
|
|
103
|
-
this.router.addMiddleware(this.myMiddleware);
|
|
104
|
-
this.routes = [
|
|
105
|
-
{
|
|
106
|
-
route: '/',
|
|
107
|
-
handlers: { // if you need reference to 'this' in your handler, remember to bind
|
|
108
|
-
get: this.myRequestHandler.bind(this)
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
route: '/two',
|
|
113
|
-
handlers: { // example of route-level middleware
|
|
114
|
-
post: [ this.myOtherMiddleware, this.myPostHandler ]
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
];
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Custom route configuration
|
|
121
|
-
*/
|
|
122
|
-
async setValues() {
|
|
123
|
-
// @note other values omitted for brevity
|
|
124
|
-
this.routes = [
|
|
125
|
-
{
|
|
126
|
-
route: '/',
|
|
127
|
-
schemaName: 'myschema', // can specify custom schema/collection like this
|
|
128
|
-
collectionName: 'myothercollection',
|
|
129
|
-
handlers: {
|
|
130
|
-
get: this.myRequestHandler.bind(this)
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
];
|
|
134
|
-
}
|
|
135
|
-
```
|
|
1
|
+
# Writing an API
|
|
2
|
+
|
|
3
|
+
_**Note:** before using this functionality, it is worth having an understanding of how to write basic custom modules first. See [this page](`adapt-authoring-api` module) for more._
|
|
4
|
+
|
|
5
|
+
The `adapt-authoring-api` module makes defining custom APIs simple by providing abstract classes and utilities you can use in your own modules to replace the common boilerplate code required when writing an API.
|
|
6
|
+
|
|
7
|
+
By extending the [AbstractAPIModule](../class/adapt_authoring_restructure/adapt-authoring-api/lib/module.js~AbstractApiModule.html) class, you get the following as standard:
|
|
8
|
+
- Boiler-plate code for defining router and endpoints
|
|
9
|
+
- Default handlers for incoming HTTP requests (with support for querying)
|
|
10
|
+
- Support for custom middleware
|
|
11
|
+
- Auto-loading of database schemas
|
|
12
|
+
- Automated (and overridable) database interaction
|
|
13
|
+
- Default permissions
|
|
14
|
+
|
|
15
|
+
## Defining your API
|
|
16
|
+
|
|
17
|
+
To define an API, all you need to do is override the `setValues` function, making sure to change the values as appropriate.
|
|
18
|
+
|
|
19
|
+
See the below table for all values:
|
|
20
|
+
|
|
21
|
+
| Attribute | Type | Description | Optional |
|
|
22
|
+
| --------- | ---- | ----------- | -------- |
|
|
23
|
+
| `root` | `String` | This value will be used as the route for any URLs (e.g. `/api/mymodule`). For APIs which deal with collections of data, the general rule is to use the plural form (ie. `/api/items`). | `false` |
|
|
24
|
+
| `router` | `Router` | The Router instance used for HTTP requests. If not defined, a new router will be created using the `root` value. | `true` |
|
|
25
|
+
| `routes` | `Array<ApiRoute>` | Definitions for any routes to be added to the API router. | `false` |
|
|
26
|
+
| `collectionName` | `String` | Default DB collection to store data to (can be overridden by individual handlers). | `false` |
|
|
27
|
+
|
|
28
|
+
### Defining routes
|
|
29
|
+
|
|
30
|
+
Before your API can handle HTTP requests, you must define which routes the router should respond to. If your module extends AbstractApiModule, this is simply a case of setting the [`routes`](../class/node_modules/adapt-authoring-api/lib/abstractApiModule.js~AbstractApiModule.html#instance-member-routes) instance variable:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
this.routes = [
|
|
34
|
+
// route config here
|
|
35
|
+
]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
#### General tips
|
|
39
|
+
The Adapt server module maps to Express functionality as closely as possible, and as such adopts the same middleware/handler concepts (with a few minor changes). It is therefore very useful to have some understanding of how the Express stack works, particularly with regards to the execution order of middleware and handlers. For more details on route handling, see the [official Express documentation](https://expressjs.com/en/guide/routing.html).
|
|
40
|
+
|
|
41
|
+
Here are a few useful notes/tips:
|
|
42
|
+
|
|
43
|
+
- The `route` attribute of each route definition is very powerful, and handles params/queries/etc. in the same way as Express (see [the Express docs](https://expressjs.com/en/guide/routing.html) for more).
|
|
44
|
+
- Use the API's middleware to perform any tasks which are common to all routes, such as checking and formatting the request data
|
|
45
|
+
- You only need to specify route handlers for the routes/HTTP methods you want to enable, access will be blocked to any route/HTTP method combinations you haven't defined
|
|
46
|
+
- You can run route-specific middleware by adding it as a handler to the route config (see example 2. below)
|
|
47
|
+
|
|
48
|
+
#### Using the default route configuration
|
|
49
|
+
Instead of defining each route yourself, the AbstractApiModule class also gives you a set of default routes which you can use if you wish:
|
|
50
|
+
```js
|
|
51
|
+
[
|
|
52
|
+
// POST, no params
|
|
53
|
+
{
|
|
54
|
+
route: '/',
|
|
55
|
+
modifying: true,
|
|
56
|
+
handlers: {
|
|
57
|
+
post: this.requestHandler()
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
// GET, optional _id param
|
|
61
|
+
{
|
|
62
|
+
route: '/:_id?',
|
|
63
|
+
handlers: {
|
|
64
|
+
get: this.requestHandler()
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
// PUT/DELETE, mandatory _id param
|
|
68
|
+
{
|
|
69
|
+
route: '/:_id',
|
|
70
|
+
modifying: true,
|
|
71
|
+
handlers: {
|
|
72
|
+
put: this.requestHandler(),
|
|
73
|
+
delete: this.requestHandler()
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
// POST custom query handler
|
|
77
|
+
{
|
|
78
|
+
route: '/query',
|
|
79
|
+
validate: false,
|
|
80
|
+
handlers: {
|
|
81
|
+
post: this.queryHandler()
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
```
|
|
86
|
+
To use the above configuration, you simply need to call the `useDefaultRouteConfig` function:
|
|
87
|
+
```js
|
|
88
|
+
async setValues() {
|
|
89
|
+
this.useDefaultRouteConfig();
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Example configurations
|
|
94
|
+
```js
|
|
95
|
+
/**
|
|
96
|
+
* Basic configuration
|
|
97
|
+
*/
|
|
98
|
+
async setValues() {
|
|
99
|
+
const server = await this.app.waitForModule('server');
|
|
100
|
+
this.root = 'myapi';
|
|
101
|
+
this.collectionName = 'mycollection';
|
|
102
|
+
this.router = server.api.createChildRouter('myapi'); // optional
|
|
103
|
+
this.router.addMiddleware(this.myMiddleware);
|
|
104
|
+
this.routes = [
|
|
105
|
+
{
|
|
106
|
+
route: '/',
|
|
107
|
+
handlers: { // if you need reference to 'this' in your handler, remember to bind
|
|
108
|
+
get: this.myRequestHandler.bind(this)
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
route: '/two',
|
|
113
|
+
handlers: { // example of route-level middleware
|
|
114
|
+
post: [ this.myOtherMiddleware, this.myPostHandler ]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Custom route configuration
|
|
121
|
+
*/
|
|
122
|
+
async setValues() {
|
|
123
|
+
// @note other values omitted for brevity
|
|
124
|
+
this.routes = [
|
|
125
|
+
{
|
|
126
|
+
route: '/',
|
|
127
|
+
schemaName: 'myschema', // can specify custom schema/collection like this
|
|
128
|
+
collectionName: 'myothercollection',
|
|
129
|
+
handlers: {
|
|
130
|
+
get: this.myRequestHandler.bind(this)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
```
|
package/errors/errors.json
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"API_MODULE_INVALID_CLASS": {
|
|
3
|
-
"data": {
|
|
4
|
-
"name": "Name of the module"
|
|
5
|
-
},
|
|
6
|
-
"description": "Module does not extend AbstractApiModule",
|
|
7
|
-
"statusCode": 500
|
|
8
|
-
},
|
|
9
|
-
"HTTP_METHOD_NOT_SUPPORTED": {
|
|
10
|
-
"data": {
|
|
11
|
-
"method": "The invalid HTTP method"
|
|
12
|
-
},
|
|
13
|
-
"description": "HTTP method for a given request is not supported",
|
|
14
|
-
"statusCode": 404
|
|
15
|
-
},
|
|
16
|
-
"NO_COLL_NAME": {
|
|
17
|
-
"description": "module is missing a collection name",
|
|
18
|
-
"statusCode": 500
|
|
19
|
-
},
|
|
20
|
-
"NO_ROOT_OR_ROUTER_DEF": {
|
|
21
|
-
"description": "module is missing both a root and router definition",
|
|
22
|
-
"statusCode": 500
|
|
23
|
-
},
|
|
24
|
-
"NO_ROUTES_DEF": {
|
|
25
|
-
"description": "module is missing API routes definition",
|
|
26
|
-
"statusCode": 500
|
|
27
|
-
},
|
|
28
|
-
"NO_SCHEMA_DEF": {
|
|
29
|
-
"description": "No json schema has been defined",
|
|
30
|
-
"statusCode": 404
|
|
31
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"API_MODULE_INVALID_CLASS": {
|
|
3
|
+
"data": {
|
|
4
|
+
"name": "Name of the module"
|
|
5
|
+
},
|
|
6
|
+
"description": "Module does not extend AbstractApiModule",
|
|
7
|
+
"statusCode": 500
|
|
8
|
+
},
|
|
9
|
+
"HTTP_METHOD_NOT_SUPPORTED": {
|
|
10
|
+
"data": {
|
|
11
|
+
"method": "The invalid HTTP method"
|
|
12
|
+
},
|
|
13
|
+
"description": "HTTP method for a given request is not supported",
|
|
14
|
+
"statusCode": 404
|
|
15
|
+
},
|
|
16
|
+
"NO_COLL_NAME": {
|
|
17
|
+
"description": "module is missing a collection name",
|
|
18
|
+
"statusCode": 500
|
|
19
|
+
},
|
|
20
|
+
"NO_ROOT_OR_ROUTER_DEF": {
|
|
21
|
+
"description": "module is missing both a root and router definition",
|
|
22
|
+
"statusCode": 500
|
|
23
|
+
},
|
|
24
|
+
"NO_ROUTES_DEF": {
|
|
25
|
+
"description": "module is missing API routes definition",
|
|
26
|
+
"statusCode": 500
|
|
27
|
+
},
|
|
28
|
+
"NO_SCHEMA_DEF": {
|
|
29
|
+
"description": "No json schema has been defined",
|
|
30
|
+
"statusCode": 404
|
|
31
|
+
}
|
|
32
32
|
}
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Abstract API utilities
|
|
3
|
-
* @namespace api
|
|
4
|
-
*/
|
|
5
|
-
export { default as AbstractApiModule } from './lib/AbstractApiModule.js'
|
|
6
|
-
export { default as AbstractApiUtils } from './lib/AbstractApiUtils.js'
|
|
7
|
-
export { default } from './lib/AbstractApiModule.js'
|
|
1
|
+
/**
|
|
2
|
+
* Abstract API utilities
|
|
3
|
+
* @namespace api
|
|
4
|
+
*/
|
|
5
|
+
export { default as AbstractApiModule } from './lib/AbstractApiModule.js'
|
|
6
|
+
export { default as AbstractApiUtils } from './lib/AbstractApiUtils.js'
|
|
7
|
+
export { default } from './lib/AbstractApiModule.js'
|