axe-api 0.20.3 → 0.21.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Release Notes
2
2
 
3
+ ## [0.21.0 (2022-12-28)](https://github.com/axe-api/axe-api/compare/0.21.0...0.20.4)
4
+
5
+ ### Features
6
+
7
+ - Added `i18n` support. [#44](https://github.com/axe-api/axe-api/issues/44)
8
+
9
+ ## [0.20.4 (2022-12-24)](https://github.com/axe-api/axe-api/compare/0.20.4...0.20.3)
10
+
11
+ ### Fixed
12
+
13
+ - Fixed [#124](https://github.com/axe-api/axe-api/issues/124)
14
+ - Throwing errors in the `development` environment has been fixed.
15
+ - Unbuilt integration test issue has been fixed.
16
+
3
17
  ## [0.20.3 (2022-12-24)](https://github.com/axe-api/axe-api/compare/0.20.3...0.20.0)
4
18
 
5
19
  ### Fixed
@@ -19,6 +19,7 @@ const Resolvers_1 = require("../Resolvers");
19
19
  const constants_1 = require("../constants");
20
20
  const Enums_1 = require("../Enums");
21
21
  const Services_1 = require("../Services");
22
+ const Middlewares_1 = require("../Middlewares");
22
23
  class RouterBuilder {
23
24
  constructor() {
24
25
  this.getPrimaryKeyName = (model) => {
@@ -52,10 +53,10 @@ class RouterBuilder {
52
53
  generalHooks.onBeforeInit(app);
53
54
  }
54
55
  yield this.createRoutesByModelTree(modelTree, modelList);
56
+ logger.info("Express routes have been created.");
55
57
  if (generalHooks.onAfterInit) {
56
58
  generalHooks.onAfterInit(app);
57
59
  }
58
- logger.info("Express routes have been created.");
59
60
  });
60
61
  }
61
62
  createRoutesByModelTree(modelTree, modelList) {
@@ -79,8 +80,13 @@ class RouterBuilder {
79
80
  }
80
81
  const urlCreator = constants_1.API_ROUTE_TEMPLATES[handlerType];
81
82
  const url = urlCreator(yield this.getRootPrefix(), urlPrefix, resource, model.instance.primaryKey);
82
- // Detecting filters
83
- const middlewares = model.instance.getMiddlewares(handlerType);
83
+ // Creating the middleware list for the route. As default, we support some
84
+ // internal middlewares such as `Accept Language Middleware` which parse
85
+ // the "accept-language" header to use in the application general.
86
+ const middlewares = [
87
+ Middlewares_1.acceptLanguageMiddleware,
88
+ ...model.instance.getMiddlewares(handlerType),
89
+ ];
84
90
  // Adding the route to the express
85
91
  yield this.addExpressRoute(handlerType, url, middlewares, model, parentModel, relation);
86
92
  }
@@ -204,10 +210,13 @@ class RouterBuilder {
204
210
  });
205
211
  break;
206
212
  default:
207
- res.status(http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR).json({
208
- error: "An error occurred.",
209
- });
210
- break;
213
+ // We should not show the real errors on production
214
+ if (process.env.NODE_ENV === "production") {
215
+ res.status(http_status_codes_1.StatusCodes.INTERNAL_SERVER_ERROR).json({
216
+ error: "An error occurredxx.",
217
+ });
218
+ }
219
+ throw error;
211
220
  }
212
221
  }
213
222
  getResourcePath(model, relation) {
@@ -1,4 +1,3 @@
1
- /// <reference types="express" />
2
1
  import { IRequestPack } from "../Interfaces";
3
2
  declare const _default: (pack: IRequestPack) => Promise<import("express").Response<any, Record<string, any>>>;
4
3
  export default _default;
@@ -1,4 +1,3 @@
1
- /// <reference types="express" />
2
1
  import { IRequestPack } from "../Interfaces";
3
2
  declare const _default: (pack: IRequestPack) => Promise<import("express").Response<any, Record<string, any>>>;
4
3
  export default _default;
@@ -1,4 +1,3 @@
1
- /// <reference types="express" />
2
1
  import { IRequestPack } from "../Interfaces";
3
2
  declare const _default: (pack: IRequestPack) => Promise<import("express").Response<any, Record<string, any>>>;
4
3
  export default _default;
@@ -1,4 +1,3 @@
1
- /// <reference types="express" />
2
1
  import { IRequestPack } from "../Interfaces";
3
2
  declare const _default: (pack: IRequestPack) => Promise<import("express").Response<any, Record<string, any>>>;
4
3
  export default _default;
@@ -35,6 +35,9 @@ exports.default = (pack) => __awaiter(void 0, void 0, void 0, function* () {
35
35
  const formData = Object.assign(Object.assign({}, item), (0, Helpers_1.getMergedFormData)(req, fillables));
36
36
  const validationRules = model.instance.getValidationRules(requestMethod);
37
37
  if (validationRules) {
38
+ // The validation language should be set
39
+ validatorjs_1.default.useLang(req.currentLanguage.language);
40
+ // Validate the data
38
41
  const validation = new validatorjs_1.default(formData, validationRules);
39
42
  if (validation.fails()) {
40
43
  return res.status(400).json(validation.errors);
@@ -1,4 +1,3 @@
1
- /// <reference types="express" />
2
1
  import { IRequestPack } from "../Interfaces";
3
2
  declare const _default: (pack: IRequestPack) => Promise<import("express").Response<any, Record<string, any>>>;
4
3
  export default _default;
@@ -1,4 +1,3 @@
1
- /// <reference types="express" />
2
1
  import { IRequestPack } from "../Interfaces";
3
2
  declare const _default: (pack: IRequestPack) => Promise<import("express").Response<any, Record<string, any>>>;
4
3
  export default _default;
@@ -22,6 +22,9 @@ exports.default = (pack) => __awaiter(void 0, void 0, void 0, function* () {
22
22
  const formData = (0, Helpers_1.getMergedFormData)(req, fillables);
23
23
  const validationRules = model.instance.getValidationRules(requestMethod);
24
24
  if (validationRules) {
25
+ // The validation language should be set
26
+ validatorjs_1.default.useLang(req.currentLanguage.language);
27
+ // Validate the data
25
28
  const validation = new validatorjs_1.default(formData, validationRules);
26
29
  if (validation.fails()) {
27
30
  return res.status(400).json(validation.errors);
@@ -1,4 +1,3 @@
1
- /// <reference types="express" />
2
1
  import { IRequestPack } from "../Interfaces";
3
2
  declare const _default: (pack: IRequestPack) => Promise<import("express").Response<any, Record<string, any>>>;
4
3
  export default _default;
@@ -35,6 +35,9 @@ exports.default = (pack) => __awaiter(void 0, void 0, void 0, function* () {
35
35
  const formData = (0, Helpers_1.getMergedFormData)(req, fillables);
36
36
  const validationRules = model.instance.getValidationRules(requestMethod);
37
37
  if (validationRules) {
38
+ // The validation language should be set
39
+ validatorjs_1.default.useLang(req.currentLanguage.language);
40
+ // Validate the data
38
41
  const validation = new validatorjs_1.default(formData, validationRules);
39
42
  if (validation.fails()) {
40
43
  return res.status(400).json(validation.errors);
@@ -23,6 +23,17 @@ export interface IApplicationConfig extends IConfig {
23
23
  prefix: string;
24
24
  transaction: boolean | IHandlerBasedTransactionConfig | IHandlerBasedTransactionConfig[];
25
25
  serializers: ((data: any, request: Request) => void)[] | IHandlerBasedSerializer[];
26
+ supportedLanguages: string[];
27
+ defaultLanguage: string;
28
+ }
29
+ export interface ILanguage {
30
+ title: string;
31
+ language: string;
32
+ region?: string | null;
33
+ }
34
+ export interface IAcceptedLanguage {
35
+ language: ILanguage;
36
+ quality: number;
26
37
  }
27
38
  export declare type IDatabaseConfig = Knex.Config;
28
39
  export interface IFolders {
@@ -0,0 +1,3 @@
1
+ import { Request, Response, NextFunction } from "express";
2
+ declare const _default: (req: Request, res: Response, next: NextFunction) => Promise<void>;
3
+ export default _default;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const Services_1 = require("../Services");
13
+ const Resolvers_1 = require("../Resolvers");
14
+ exports.default = (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
15
+ // Application configuration is need for the default setting.
16
+ const configs = yield Services_1.IoCService.use("Config");
17
+ const application = configs.Application;
18
+ const { supportedLanguages, defaultLanguage } = application;
19
+ // Setting the current language by the supported, default and the client prefences
20
+ req.currentLanguage = Resolvers_1.AcceptLanguageResolver.resolve(req.get("accept-language") || "", supportedLanguages || ["en"], defaultLanguage || "en");
21
+ // Adding the `Content-Language` header to the response object
22
+ res.setHeader("Content-Language", req.currentLanguage.title);
23
+ next();
24
+ });
@@ -0,0 +1,2 @@
1
+ import acceptLanguageMiddleware from "./acceptLanguageMiddleware";
2
+ export { acceptLanguageMiddleware };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.acceptLanguageMiddleware = void 0;
7
+ const acceptLanguageMiddleware_1 = __importDefault(require("./acceptLanguageMiddleware"));
8
+ exports.acceptLanguageMiddleware = acceptLanguageMiddleware_1.default;
@@ -0,0 +1,7 @@
1
+ import { ILanguage } from "../Interfaces";
2
+ declare class AcceptLanguageResolver {
3
+ static resolve(value: string, supportedLanguages: string[], defaultLanguage: string): ILanguage;
4
+ static toLanguageObject(key?: string): ILanguage;
5
+ private static toSortedPreferences;
6
+ }
7
+ export default AcceptLanguageResolver;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class AcceptLanguageResolver {
4
+ static resolve(value, supportedLanguages, defaultLanguage) {
5
+ value = value.trim();
6
+ if (value === "*") {
7
+ return this.toLanguageObject(defaultLanguage);
8
+ }
9
+ const languages = this.toSortedPreferences(value);
10
+ const perfectMatch = languages.find((item) => supportedLanguages.includes(item.language.title));
11
+ const anyMatch = languages.find((item) => supportedLanguages.includes(item.language.language));
12
+ if (perfectMatch && !anyMatch) {
13
+ return perfectMatch.language;
14
+ }
15
+ if (!perfectMatch && anyMatch) {
16
+ return anyMatch.language;
17
+ }
18
+ if (perfectMatch && anyMatch) {
19
+ if (perfectMatch.quality >= anyMatch.quality) {
20
+ return perfectMatch.language;
21
+ }
22
+ else {
23
+ return this.toLanguageObject(anyMatch.language.language);
24
+ }
25
+ }
26
+ return this.toLanguageObject(defaultLanguage);
27
+ }
28
+ static toLanguageObject(key = "") {
29
+ const [language, region] = key.split("-");
30
+ return {
31
+ title: key,
32
+ language,
33
+ region: region || null,
34
+ };
35
+ }
36
+ static toSortedPreferences(value) {
37
+ // Splitting by language definitons
38
+ const keys = value.split(",").map((key) => key.trim());
39
+ const languages = [];
40
+ for (const key of keys) {
41
+ // Splitting by the quality values
42
+ const [code, quality] = key.split(";");
43
+ // Parsing the language code and the quality value
44
+ const item = {
45
+ language: this.toLanguageObject(code),
46
+ quality: quality ? parseFloat(quality.replace("q=", "")) : 1,
47
+ };
48
+ languages.push(item);
49
+ }
50
+ // Sorting ASC
51
+ languages.sort((a, b) => {
52
+ if (a.quality === b.quality) {
53
+ return 0;
54
+ }
55
+ if (a.quality === null) {
56
+ return 1;
57
+ }
58
+ if (b.quality === null) {
59
+ return -1;
60
+ }
61
+ return a.quality < b.quality ? 1 : -1;
62
+ });
63
+ return languages;
64
+ }
65
+ }
66
+ exports.default = AcceptLanguageResolver;
@@ -1,7 +1,8 @@
1
+ import AcceptLanguageResolver from "./AcceptLanguageResolver";
1
2
  import FileResolver from "./FileResolver";
2
3
  import FolderResolver from "./FolderResolver";
3
4
  import GeneralHookResolver from "./GeneralHookResolver";
4
5
  import ModelResolver from "./ModelResolver";
5
6
  import TransactionResolver from "./TransactionResolver";
6
7
  import WithQueryResolver from "./WithQueryResolver";
7
- export { FileResolver, FolderResolver, GeneralHookResolver, ModelResolver, TransactionResolver, WithQueryResolver, };
8
+ export { AcceptLanguageResolver, FileResolver, FolderResolver, GeneralHookResolver, ModelResolver, TransactionResolver, WithQueryResolver, };
@@ -3,7 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.WithQueryResolver = exports.TransactionResolver = exports.ModelResolver = exports.GeneralHookResolver = exports.FolderResolver = exports.FileResolver = void 0;
6
+ exports.WithQueryResolver = exports.TransactionResolver = exports.ModelResolver = exports.GeneralHookResolver = exports.FolderResolver = exports.FileResolver = exports.AcceptLanguageResolver = void 0;
7
+ const AcceptLanguageResolver_1 = __importDefault(require("./AcceptLanguageResolver"));
8
+ exports.AcceptLanguageResolver = AcceptLanguageResolver_1.default;
7
9
  const FileResolver_1 = __importDefault(require("./FileResolver"));
8
10
  exports.FileResolver = FileResolver_1.default;
9
11
  const FolderResolver_1 = __importDefault(require("./FolderResolver"));
@@ -346,7 +346,17 @@ class QueryService {
346
346
  if (!relation) {
347
347
  throw new ApiError_1.default(`Undefined relation: ${item.relationship}`);
348
348
  }
349
- this.relationColumns.push(`${this.model.instance.table}.${relation.foreignKey}`);
349
+ // We should add the field by the relation type. Otherwise it can cause
350
+ // an error something like this; `users.user_id`. Foreign and primary keys
351
+ // are relative.
352
+ let columnName = "";
353
+ if (relation.type === Enums_1.Relationships.HAS_MANY) {
354
+ columnName = relation.primaryKey;
355
+ }
356
+ else {
357
+ columnName = relation.foreignKey;
358
+ }
359
+ this.relationColumns.push(`${this.model.instance.table}.${columnName}`);
350
360
  });
351
361
  }
352
362
  getConditionMethodName(ruleSet) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axe-api",
3
- "version": "0.20.3",
3
+ "version": "0.21.0",
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": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -15,15 +15,18 @@
15
15
  "scripts": {
16
16
  "build": "tsc",
17
17
  "build:watch": "tsc -w",
18
- "dev": "ts-node-dev --respawn --clear index.ts",
18
+ "dev": "ts-node-dev --files --respawn --clear index.ts",
19
+ "dev-kit": "ts-node-dev --respawn --clear dev-kit.ts",
20
+ "dev-kit:install": "sh ./scripts/dev-kit-install.sh",
21
+ "dev-kit:remove": "sh ./scripts/dev-kit-remove.sh",
19
22
  "test": "jest --runInBand",
20
23
  "test:dev": "jest --watch",
21
24
  "lint": "eslint src/**",
22
25
  "lint:watch": "esw --watch --color",
23
26
  "prepare:integration": "nodemon --ignore \"./tests/**\" ./scripts/run-integration-test.js",
24
- "test:integration:mysql8": "cd ./tests/integrations && node index.js mysql8",
25
- "test:integration:mysql57": "cd ./tests/integrations && node index.js mysql57",
26
- "test:integration:postgres": "cd ./tests/integrations && node index.js postgres"
27
+ "test:postgres": "sh ./scripts/test-postgres.sh",
28
+ "test:mysql57": "sh ./scripts/test-mysql57.sh",
29
+ "test:mysql8": "sh ./scripts/test-mysql8.sh"
27
30
  },
28
31
  "dependencies": {
29
32
  "@types/express": "^4.17.15",
@@ -41,6 +44,7 @@
41
44
  "@babel/core": "^7.19.1",
42
45
  "@babel/preset-env": "^7.19.1",
43
46
  "@babel/preset-typescript": "^7.18.6",
47
+ "@types/accept-language-parser": "^1.5.3",
44
48
  "@types/pluralize": "^0.0.29",
45
49
  "@types/validatorjs": "^3.15.0",
46
50
  "@typescript-eslint/eslint-plugin": "^5.37.0",
@@ -56,6 +60,7 @@
56
60
  "glob": "^8.0.3",
57
61
  "jest": "^29.0.3",
58
62
  "mysql": "^2.18.1",
63
+ "node-color-log": "^10.0.2",
59
64
  "nodemon": "^2.0.15",
60
65
  "pg": "^8.7.1",
61
66
  "set-value": ">=4.1.0",
package/readme.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <h1 align="center">
2
2
  <br>
3
- <a href="https://axe-api.github.io/">
4
- <img src="https://axe-api.github.io/axe.png" alt="Markdownify" width="200">
3
+ <a href="https://axe-api.com/">
4
+ <img src="https://axe-api.com/axe.png" alt="Markdownify" width="200">
5
5
  </a>
6
6
  <br>
7
7
  Axe API
@@ -25,7 +25,7 @@
25
25
 
26
26
  Next Generation Rest API Framework
27
27
 
28
- > Axe API has great documentation. Please [check it out in here](https://axe-api.github.io/).
28
+ > Axe API has great documentation. Please [check it out in here](https://axe-api.com/).
29
29
 
30
30
  ## What Is Axe API?
31
31
 
@@ -143,7 +143,7 @@ If you can see that response, it means that your project is running properly.
143
143
 
144
144
  ## Documentation
145
145
 
146
- Axe API has great documentation. Please [check it out in here](https://axe-api.github.io/).
146
+ Axe API has great documentation. Please [check it out in here](https://axe-api.com/).
147
147
 
148
148
  ## How To Run Integration Tests
149
149