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.
@@ -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 -->)
@@ -0,0 +1,15 @@
1
+ name: Create Tag
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v2
13
+ - uses: butlerlogic/action-autotag@stable
14
+ with:
15
+ GITHUB_TOKEN: "${{ secrets.AUTO_TAG_TOKEN }}"
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 { LOG_LEVEL, HOOK_FUNCTIONS, HANDLERS } from "./src/constants.js";
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.1",
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.11",
26
- "knex-paginate": "^2.3.0",
27
- "knex-schema-inspector": "^1.6.2",
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
  };
@@ -55,7 +55,7 @@ class QueryParser {
55
55
  });
56
56
  }
57
57
 
58
- applyWheresInsideGroup(query, sub, ruleSet) {
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(query, sub, item);
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(query, sub, ruleSet);
83
+ this._applyConditionRule(sub, ruleSet);
84
84
  }
85
85
  }
86
86
 
87
87
  applyWheres(query, ruleSet) {
88
88
  query.where((sub) => {
89
- this.applyWheresInsideGroup(query, sub, ruleSet);
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(query, sub, ruleSet) {
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;
@@ -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
- handleErrors(req, res, error);
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()](url, middlewares, (req, res) => {
232
- requestHandler(handler, req, res, context);
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);