axe-api 0.17.1 → 0.17.5
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/PULL_REQUEST_TEMPLATE.md +32 -0
- package/.github/workflows/auto-tag.yml +15 -0
- package/CHANGELOG.md +25 -0
- package/index.js +7 -1
- package/package.json +4 -4
- package/readme.md +24 -0
- package/src/constants.js +9 -0
- package/src/core/QueryParser.js +32 -8
- package/src/handlers/autosave.js +1 -1
- package/src/resolvers/setExpressRoutes.js +11 -25
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
|
|
3
|
+
<!--- Describe your changes in detail -->
|
|
4
|
+
|
|
5
|
+
## Motivation and Context
|
|
6
|
+
|
|
7
|
+
<!--- Why is this change required? What problem does it solve? -->
|
|
8
|
+
<!--- If it fixes an open issue, please link to the issue here. -->
|
|
9
|
+
|
|
10
|
+
## How has this been tested?
|
|
11
|
+
|
|
12
|
+
<!--- Please describe in detail how you tested your changes. -->
|
|
13
|
+
<!--- Include details of your testing environment, tests ran to see how -->
|
|
14
|
+
<!--- your change affects other areas of the code, etc. -->
|
|
15
|
+
|
|
16
|
+
## Types of changes
|
|
17
|
+
|
|
18
|
+
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
|
19
|
+
|
|
20
|
+
- [ ] Bug fix (non-breaking change which fixes an issue)
|
|
21
|
+
- [ ] New feature (non-breaking change which adds functionality)
|
|
22
|
+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
|
23
|
+
|
|
24
|
+
## Checklist:
|
|
25
|
+
|
|
26
|
+
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
|
27
|
+
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
|
28
|
+
|
|
29
|
+
- [ ] My changes requires documentation change.
|
|
30
|
+
- [ ] The changes have been documented in the [axe-api/docs](https://github.com/axe-api/docs) repository. (<!--- Please insert the PR link -->)
|
|
31
|
+
- [ ] I added integration tests properly.
|
|
32
|
+
- [ ] The changes require [axe-api-template](https://github.com/axe-api/axe-api-template) changes. (<!--- Please insert the PR link -->)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
|
+
## [0.17.5 (2021-11-27)](https://github.com/axe-api/axe-api/compare/0.17.5...0.17.4)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- [#111](https://github.com/axe-api/axe-api/issues/111)
|
|
8
|
+
|
|
9
|
+
## [0.17.4 (2021-10-28)](https://github.com/axe-api/axe-api/compare/0.17.4...0.17.3)
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- [#97](https://github.com/axe-api/axe-api/issues/97)
|
|
14
|
+
- [#104](https://github.com/axe-api/axe-api/issues/104)
|
|
15
|
+
|
|
16
|
+
## [0.17.3 (2021-10-28)](https://github.com/axe-api/axe-api/compare/0.17.3...0.17.2)
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- [#98](https://github.com/axe-api/axe-api/issues/98)
|
|
21
|
+
|
|
22
|
+
## [0.17.2 (2021-10-17)](https://github.com/axe-api/axe-api/compare/0.17.2...0.17.1)
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Fixed table join on the related table filter.
|
|
27
|
+
|
|
3
28
|
## [0.17.1 (2021-10-17)](https://github.com/axe-api/axe-api/compare/0.17.1...0.17.0)
|
|
4
29
|
|
|
5
30
|
### Fixed
|
package/index.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import Server from "./src/Server.js";
|
|
2
2
|
import Model from "./src/core/Model.js";
|
|
3
3
|
import IoC from "./src/core/IoC.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
LOG_LEVEL,
|
|
6
|
+
HOOK_FUNCTIONS,
|
|
7
|
+
HANDLERS,
|
|
8
|
+
DEFAULT_HANDLERS,
|
|
9
|
+
} from "./src/constants.js";
|
|
5
10
|
import HttpResponse from "./src/core/HttpResponse.js";
|
|
6
11
|
|
|
7
12
|
export {
|
|
@@ -12,4 +17,5 @@ export {
|
|
|
12
17
|
LOG_LEVEL,
|
|
13
18
|
HOOK_FUNCTIONS,
|
|
14
19
|
HANDLERS,
|
|
20
|
+
DEFAULT_HANDLERS,
|
|
15
21
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "axe-api",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.5",
|
|
4
4
|
"description": "AXE API is a simple tool which has been created based on Express and Knex.js to create Rest APIs quickly.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"change-case": "^4.1.2",
|
|
23
23
|
"dotenv": "^10.0.0",
|
|
24
24
|
"express": "^4.17.1",
|
|
25
|
-
"knex": "^0.95.
|
|
26
|
-
"knex-paginate": "^
|
|
27
|
-
"knex-schema-inspector": "^1.6.
|
|
25
|
+
"knex": "^0.95.12",
|
|
26
|
+
"knex-paginate": "^3.0.0",
|
|
27
|
+
"knex-schema-inspector": "^1.6.4",
|
|
28
28
|
"pluralize": "^8.0.0",
|
|
29
29
|
"validatorjs": "^3.22.1"
|
|
30
30
|
},
|
package/readme.md
CHANGED
|
@@ -39,6 +39,30 @@ Fastest way to create simple Rest API by defining database models and relations.
|
|
|
39
39
|
- Multiple database support (Postgres, MSSQL, MySQL, MariaDB, SQLite3, Oracle, and Amazon Redshift)
|
|
40
40
|
- Well documented
|
|
41
41
|
|
|
42
|
+
## How To Run Integration Tests
|
|
43
|
+
|
|
44
|
+
> You have to have **Docker** and **Docker Compose** on your local development environment to run integration tests.
|
|
45
|
+
|
|
46
|
+
Execute the following commands to prepare the integration app
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
cd tests/integrations && npm install && npm ci && npm run build --if-present
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Execute the following commands to prepare the database;
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
docker-compose -f "./tests/integrations/docker-compose.mysql8.yml" up -d --build
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
> To down the database, you can use the following command; `docker-compose -f "./tests/integrations/docker-compose.mysql8.yml" up -d --build`
|
|
59
|
+
|
|
60
|
+
You can execute the following command to execute tests;
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
npm run test:integration:mysql8
|
|
64
|
+
```
|
|
65
|
+
|
|
42
66
|
## License
|
|
43
67
|
|
|
44
68
|
[MIT License](LICENSE)
|
package/src/constants.js
CHANGED
|
@@ -55,6 +55,14 @@ const HANDLERS = {
|
|
|
55
55
|
AUTOSAVE: "autosave",
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
+
const DEFAULT_HANDLERS = [
|
|
59
|
+
HANDLERS.INSERT,
|
|
60
|
+
HANDLERS.PAGINATE,
|
|
61
|
+
HANDLERS.SHOW,
|
|
62
|
+
HANDLERS.UPDATE,
|
|
63
|
+
HANDLERS.DELETE,
|
|
64
|
+
];
|
|
65
|
+
|
|
58
66
|
const HTTP_METHODS = {
|
|
59
67
|
POST: "POST",
|
|
60
68
|
GET: "GET",
|
|
@@ -125,5 +133,6 @@ export {
|
|
|
125
133
|
LOG_COLORS,
|
|
126
134
|
DEPENDECY_TYPES,
|
|
127
135
|
HANDLERS,
|
|
136
|
+
DEFAULT_HANDLERS,
|
|
128
137
|
TIMESTAMP_COLUMNS,
|
|
129
138
|
};
|
package/src/core/QueryParser.js
CHANGED
|
@@ -55,7 +55,7 @@ class QueryParser {
|
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
applyWheresInsideGroup(
|
|
58
|
+
applyWheresInsideGroup(sub, ruleSet) {
|
|
59
59
|
// If there is not any query, we don't have to filter the data.
|
|
60
60
|
if (!ruleSet) {
|
|
61
61
|
return;
|
|
@@ -65,7 +65,7 @@ class QueryParser {
|
|
|
65
65
|
for (const item of ruleSet) {
|
|
66
66
|
// If the item is not an array, it means that it is a standard condition
|
|
67
67
|
if (Array.isArray(item) === false) {
|
|
68
|
-
this._applyConditionRule(
|
|
68
|
+
this._applyConditionRule(sub, item);
|
|
69
69
|
} else {
|
|
70
70
|
// If the item is an array, we should create the query recursively.
|
|
71
71
|
if (item[0].prefix === "or") {
|
|
@@ -80,14 +80,16 @@ class QueryParser {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
} else {
|
|
83
|
-
this._applyConditionRule(
|
|
83
|
+
this._applyConditionRule(sub, ruleSet);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
applyWheres(query, ruleSet) {
|
|
88
88
|
query.where((sub) => {
|
|
89
|
-
this.applyWheresInsideGroup(
|
|
89
|
+
this.applyWheresInsideGroup(sub, ruleSet);
|
|
90
90
|
});
|
|
91
|
+
|
|
92
|
+
this._applyRelatedQueryJoins(query, ruleSet);
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
get(query) {
|
|
@@ -124,15 +126,12 @@ class QueryParser {
|
|
|
124
126
|
];
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
_applyConditionRule(
|
|
129
|
+
_applyConditionRule(sub, ruleSet) {
|
|
128
130
|
const method = this._getConditionMethodName(ruleSet);
|
|
129
131
|
const zeroArguments = ["Null", "NotNull"];
|
|
130
132
|
const oneArguments = ["In", "NotIn", "Between", "NotBetween"];
|
|
131
133
|
|
|
132
134
|
const fullFieldPath = `${ruleSet.table}.${ruleSet.field}`;
|
|
133
|
-
if (ruleSet.table !== this.model.instance.table) {
|
|
134
|
-
this._addJoinOnce(query, ruleSet);
|
|
135
|
-
}
|
|
136
135
|
|
|
137
136
|
if (zeroArguments.indexOf(ruleSet.condition) > -1) {
|
|
138
137
|
return sub[`${method}${ruleSet.condition}`](fullFieldPath);
|
|
@@ -145,6 +144,31 @@ class QueryParser {
|
|
|
145
144
|
return sub[method](fullFieldPath, ruleSet.condition, ruleSet.value);
|
|
146
145
|
}
|
|
147
146
|
|
|
147
|
+
_applyRelatedQueryJoins(query, ruleSet) {
|
|
148
|
+
if (!ruleSet) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (Array.isArray(ruleSet)) {
|
|
153
|
+
for (const item of ruleSet) {
|
|
154
|
+
// If the item is not an array, it means that it is a standard condition
|
|
155
|
+
if (Array.isArray(item) === false) {
|
|
156
|
+
this._applyJoinIfNecessary(query, item);
|
|
157
|
+
} else {
|
|
158
|
+
this._applyRelatedQueryJoins(query, item);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
} else {
|
|
162
|
+
this._applyJoinIfNecessary(query, ruleSet);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
_applyJoinIfNecessary(query, ruleSet) {
|
|
167
|
+
if (ruleSet.table !== this.model.instance.table) {
|
|
168
|
+
this._addJoinOnce(query, ruleSet);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
148
172
|
_addJoinOnce(query, { model, relation }) {
|
|
149
173
|
if (this.createdJoins.includes(relation.name)) {
|
|
150
174
|
return;
|
package/src/handlers/autosave.js
CHANGED
|
@@ -78,7 +78,7 @@ export default async (context) => {
|
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
// Serializing the data by the model's serialize method
|
|
81
|
-
item = serializeData(item, model.instance.serialize);
|
|
81
|
+
item = await serializeData(item, model.instance.serialize);
|
|
82
82
|
|
|
83
83
|
// Filtering hidden fields from the response data.
|
|
84
84
|
filterHiddenFields([item], model.instance.hiddens);
|
|
@@ -1,29 +1,11 @@
|
|
|
1
1
|
import IoC from "./../core/IoC.js";
|
|
2
2
|
import pluralize from "pluralize";
|
|
3
|
+
import { paramCase } from "change-case";
|
|
3
4
|
import { RELATIONSHIPS, API_ROUTE_TEMPLATES } from "./../constants.js";
|
|
4
5
|
import Handlers from "./../handlers/index.js";
|
|
5
6
|
|
|
6
7
|
let Config = null;
|
|
7
8
|
|
|
8
|
-
const handleErrors = (req, res, error) => {
|
|
9
|
-
const status = error.type === "HttpResponse" ? error.status : 400;
|
|
10
|
-
let errors = error.content ? error.content : error.message;
|
|
11
|
-
|
|
12
|
-
if (Config.Application.env === "production") {
|
|
13
|
-
errors = "An error occurred!";
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const result = {
|
|
17
|
-
errors,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
if (Config.Application.env === "development") {
|
|
21
|
-
result.stack = error.stack;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
res.status(status).json(result);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
9
|
const setTransactionOption = (option, handler, defaultValue) => {
|
|
28
10
|
if (Array.isArray(option)) {
|
|
29
11
|
if (option.some((i) => i.handler === handler)) {
|
|
@@ -52,7 +34,7 @@ const hasTransaction = (config, model, handler) => {
|
|
|
52
34
|
return privilegedOption;
|
|
53
35
|
};
|
|
54
36
|
|
|
55
|
-
const requestHandler = async (handler, req, res, context) => {
|
|
37
|
+
const requestHandler = async (handler, req, res, next, context) => {
|
|
56
38
|
try {
|
|
57
39
|
context.trx = context.database;
|
|
58
40
|
if (hasTransaction(Config, context.model, handler)) {
|
|
@@ -72,7 +54,7 @@ const requestHandler = async (handler, req, res, context) => {
|
|
|
72
54
|
await context.trx.rollback();
|
|
73
55
|
}
|
|
74
56
|
|
|
75
|
-
|
|
57
|
+
next(error);
|
|
76
58
|
}
|
|
77
59
|
};
|
|
78
60
|
|
|
@@ -83,7 +65,7 @@ const ucFirst = (string) => {
|
|
|
83
65
|
const getResourcePath = (model, relation) => {
|
|
84
66
|
return relation
|
|
85
67
|
? relation.resource
|
|
86
|
-
: pluralize.plural(model.name).toLowerCase();
|
|
68
|
+
: paramCase(pluralize.plural(model.name)).toLowerCase();
|
|
87
69
|
};
|
|
88
70
|
|
|
89
71
|
const getPrimaryKeyName = (model) => {
|
|
@@ -228,9 +210,13 @@ const createRouteByModel = async (
|
|
|
228
210
|
docs.push(routeTemplate.method, url, model);
|
|
229
211
|
|
|
230
212
|
// Adding the route to the express
|
|
231
|
-
app[routeTemplate.method.toLowerCase()](
|
|
232
|
-
|
|
233
|
-
|
|
213
|
+
app[routeTemplate.method.toLowerCase()](
|
|
214
|
+
url,
|
|
215
|
+
middlewares,
|
|
216
|
+
(req, res, next) => {
|
|
217
|
+
requestHandler(handler, req, res, next, context);
|
|
218
|
+
}
|
|
219
|
+
);
|
|
234
220
|
}
|
|
235
221
|
|
|
236
222
|
await createChildRoutes(model, models, resource, urlPrefix);
|