axe-api 0.20.0-rc2 → 0.20.0-rc21
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/build/index.d.ts +2 -1
- package/build/index.js +3 -1
- package/build/src/Builders/RouterBuilder.d.ts +0 -2
- package/build/src/Builders/RouterBuilder.js +19 -46
- package/build/src/Handlers/Helpers.d.ts +1 -1
- package/build/src/Handlers/Helpers.js +17 -11
- package/build/src/Interfaces.d.ts +8 -7
- package/build/src/Model.d.ts +2 -2
- package/build/src/Resolvers/FileResolver.js +30 -3
- package/build/src/Resolvers/GeneralHookResolver.d.ts +5 -0
- package/build/src/Resolvers/GeneralHookResolver.js +35 -0
- package/build/src/Resolvers/ModelMiddlewareResolver.d.ts +7 -0
- package/build/src/Resolvers/ModelMiddlewareResolver.js +29 -0
- package/build/src/Resolvers/ModelResolver.js +7 -1
- package/build/src/Resolvers/TransactionResolver.d.ts +8 -0
- package/build/src/Resolvers/TransactionResolver.js +75 -0
- package/build/src/Resolvers/WithQueryResolver.d.ts +13 -0
- package/build/src/Resolvers/WithQueryResolver.js +116 -0
- package/build/src/Resolvers/index.d.ts +5 -1
- package/build/src/Resolvers/index.js +9 -1
- package/build/src/Services/QueryService.d.ts +19 -24
- package/build/src/Services/QueryService.js +58 -131
- package/package.json +8 -4
package/build/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import Server from "./src/Server";
|
|
|
2
2
|
import Model from "./src/Model";
|
|
3
3
|
import ApiError from "./src/Exceptions/ApiError";
|
|
4
4
|
import { DEFAULT_HANDLERS } from "./src/constants";
|
|
5
|
+
import { IoCService } from "./src/Services";
|
|
5
6
|
export * from "./src/Enums";
|
|
6
7
|
export * from "./src/Interfaces";
|
|
7
|
-
export { Server, Model, ApiError, DEFAULT_HANDLERS };
|
|
8
|
+
export { Server, Model, ApiError, DEFAULT_HANDLERS, IoCService };
|
package/build/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.DEFAULT_HANDLERS = exports.ApiError = exports.Model = exports.Server = void 0;
|
|
20
|
+
exports.IoCService = exports.DEFAULT_HANDLERS = exports.ApiError = exports.Model = exports.Server = void 0;
|
|
21
21
|
const Server_1 = __importDefault(require("./src/Server"));
|
|
22
22
|
exports.Server = Server_1.default;
|
|
23
23
|
const Model_1 = __importDefault(require("./src/Model"));
|
|
@@ -26,5 +26,7 @@ const ApiError_1 = __importDefault(require("./src/Exceptions/ApiError"));
|
|
|
26
26
|
exports.ApiError = ApiError_1.default;
|
|
27
27
|
const constants_1 = require("./src/constants");
|
|
28
28
|
Object.defineProperty(exports, "DEFAULT_HANDLERS", { enumerable: true, get: function () { return constants_1.DEFAULT_HANDLERS; } });
|
|
29
|
+
const Services_1 = require("./src/Services");
|
|
30
|
+
Object.defineProperty(exports, "IoCService", { enumerable: true, get: function () { return Services_1.IoCService; } });
|
|
29
31
|
__exportStar(require("./src/Enums"), exports);
|
|
30
32
|
__exportStar(require("./src/Interfaces"), exports);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
declare class RouterBuilder {
|
|
2
2
|
build(): Promise<void>;
|
|
3
|
-
private getGeneralHooks;
|
|
4
3
|
private createRoutesByModelTree;
|
|
5
4
|
private createRouteByModel;
|
|
6
5
|
private createNestedRoutes;
|
|
@@ -12,6 +11,5 @@ declare class RouterBuilder {
|
|
|
12
11
|
private sendErrorAsResponse;
|
|
13
12
|
private getResourcePath;
|
|
14
13
|
private getRootPrefix;
|
|
15
|
-
private getModelMiddlewares;
|
|
16
14
|
}
|
|
17
15
|
export default RouterBuilder;
|
|
@@ -12,7 +12,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
const path_1 = __importDefault(require("path"));
|
|
16
15
|
const pluralize_1 = __importDefault(require("pluralize"));
|
|
17
16
|
const http_status_codes_1 = require("http-status-codes");
|
|
18
17
|
const change_case_1 = require("change-case");
|
|
@@ -44,35 +43,21 @@ class RouterBuilder {
|
|
|
44
43
|
}
|
|
45
44
|
build() {
|
|
46
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
const app = yield Services_1.IoCService.useByType("App");
|
|
47
47
|
const logger = yield Services_1.IoCService.useByType("LogService");
|
|
48
48
|
const modelTree = yield Services_1.IoCService.useByType("ModelTree");
|
|
49
49
|
const modelList = yield Services_1.IoCService.useByType("ModelListService");
|
|
50
|
-
const generalHooks = yield
|
|
50
|
+
const generalHooks = yield Resolvers_1.GeneralHookResolver.resolve();
|
|
51
51
|
if (generalHooks.onBeforeInit) {
|
|
52
|
-
generalHooks.onBeforeInit();
|
|
52
|
+
generalHooks.onBeforeInit(app);
|
|
53
53
|
}
|
|
54
54
|
yield this.createRoutesByModelTree(modelTree, modelList);
|
|
55
55
|
if (generalHooks.onAfterInit) {
|
|
56
|
-
generalHooks.onAfterInit();
|
|
56
|
+
generalHooks.onAfterInit(app);
|
|
57
57
|
}
|
|
58
58
|
logger.info("Express routes have been created.");
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
-
getGeneralHooks() {
|
|
62
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
-
const folders = (yield Services_1.IoCService.use("Folders"));
|
|
64
|
-
const fileResolver = new Resolvers_1.FileResolver();
|
|
65
|
-
const content = yield fileResolver.resolveContent(path_1.default.join(folders.App, "app"));
|
|
66
|
-
if (content && content.init) {
|
|
67
|
-
const { onBeforeInit = null, onAfterInit = null } = content.init;
|
|
68
|
-
return { onBeforeInit, onAfterInit };
|
|
69
|
-
}
|
|
70
|
-
return {
|
|
71
|
-
onBeforeInit: null,
|
|
72
|
-
onAfterInit: null,
|
|
73
|
-
};
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
61
|
createRoutesByModelTree(modelTree, modelList) {
|
|
77
62
|
return __awaiter(this, void 0, void 0, function* () {
|
|
78
63
|
for (const model of modelTree) {
|
|
@@ -95,7 +80,7 @@ class RouterBuilder {
|
|
|
95
80
|
const urlCreator = constants_1.API_ROUTE_TEMPLATES[handlerType];
|
|
96
81
|
const url = urlCreator(yield this.getRootPrefix(), urlPrefix, resource, model.instance.primaryKey);
|
|
97
82
|
// Detecting filters
|
|
98
|
-
const middlewares =
|
|
83
|
+
const middlewares = Resolvers_1.ModelMiddlewareResolver.resolve(model, handlerType);
|
|
99
84
|
// Adding the route to the express
|
|
100
85
|
yield this.addExpressRoute(handlerType, url, middlewares, model, parentModel, relation);
|
|
101
86
|
}
|
|
@@ -176,9 +161,15 @@ class RouterBuilder {
|
|
|
176
161
|
}
|
|
177
162
|
requestHandler(handlerType, req, res, model, parentModel, relation) {
|
|
178
163
|
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
+
let trx = null;
|
|
165
|
+
let hasTransaction = false;
|
|
179
166
|
try {
|
|
180
167
|
const factory = yield Services_1.IoCService.useByType("HandlerFactory");
|
|
181
168
|
const database = (yield Services_1.IoCService.use("Database"));
|
|
169
|
+
hasTransaction = yield Resolvers_1.TransactionResolver.resolve(model, handlerType);
|
|
170
|
+
if (hasTransaction) {
|
|
171
|
+
trx = yield database.transaction();
|
|
172
|
+
}
|
|
182
173
|
const handler = factory.get(handlerType);
|
|
183
174
|
const pack = {
|
|
184
175
|
req,
|
|
@@ -187,11 +178,17 @@ class RouterBuilder {
|
|
|
187
178
|
model,
|
|
188
179
|
parentModel,
|
|
189
180
|
relation,
|
|
190
|
-
database,
|
|
181
|
+
database: hasTransaction && trx ? trx : database,
|
|
191
182
|
};
|
|
192
183
|
yield handler(pack);
|
|
184
|
+
if (hasTransaction && trx) {
|
|
185
|
+
trx.commit();
|
|
186
|
+
}
|
|
193
187
|
}
|
|
194
188
|
catch (error) {
|
|
189
|
+
if (hasTransaction && trx) {
|
|
190
|
+
trx.rollback();
|
|
191
|
+
}
|
|
195
192
|
this.sendErrorAsResponse(res, error);
|
|
196
193
|
}
|
|
197
194
|
});
|
|
@@ -213,35 +210,11 @@ class RouterBuilder {
|
|
|
213
210
|
}
|
|
214
211
|
}
|
|
215
212
|
getResourcePath(model, relation) {
|
|
216
|
-
// TODO:
|
|
213
|
+
// TODO: Fix
|
|
217
214
|
return (0, change_case_1.paramCase)(pluralize_1.default.plural(model.name)).toLowerCase();
|
|
218
215
|
// return relation
|
|
219
216
|
// ? relation.resource
|
|
220
217
|
// : paramCase(pluralize.plural(model.name)).toLowerCase();
|
|
221
218
|
}
|
|
222
|
-
getModelMiddlewares(model, handlerType) {
|
|
223
|
-
const results = [];
|
|
224
|
-
const middlewares = model.instance.middlewares;
|
|
225
|
-
if (Array.isArray(middlewares)) {
|
|
226
|
-
middlewares.forEach((item) => {
|
|
227
|
-
if (item === null || item === void 0 ? void 0 : item.handler) {
|
|
228
|
-
const methodBasedMiddlewares = item;
|
|
229
|
-
if (methodBasedMiddlewares.handler.includes(handlerType)) {
|
|
230
|
-
results.push(methodBasedMiddlewares.middleware);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
results.push(item);
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
const methodBasedMiddlewares = middlewares;
|
|
240
|
-
if (methodBasedMiddlewares.handler.includes(handlerType)) {
|
|
241
|
-
results.push(methodBasedMiddlewares.middleware);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
return results;
|
|
245
|
-
}
|
|
246
219
|
}
|
|
247
220
|
exports.default = RouterBuilder;
|
|
@@ -11,4 +11,4 @@ export declare const getParentColumn: (relation: IRelation | null) => string | n
|
|
|
11
11
|
export declare const addForeignKeyQuery: (request: Request, query: Knex.QueryBuilder, relation: IRelation | null, parentModel: IModelService | null) => void;
|
|
12
12
|
export declare const serializeData: (itemArray: any[] | any, modelSerializer: (data: any, request: Request) => void, handler: HandlerTypes, request: Request) => Promise<any[]>;
|
|
13
13
|
export declare const filterHiddenFields: (itemArray: any[], hiddens: string[] | null) => void;
|
|
14
|
-
export declare const getRelatedData: (data: any[], withArray: IWith[], model: IModelService, modelList: ModelListService, database: Knex, handler: HandlerTypes, request: Request) => Promise<void>;
|
|
14
|
+
export declare const getRelatedData: (data: any[], withArray: IWith[], model: IModelService, modelList: ModelListService, database: Knex | Knex.Transaction, handler: HandlerTypes, request: Request) => Promise<void>;
|
|
@@ -43,16 +43,13 @@ const callHooks = (model, type, params) => __awaiter(void 0, void 0, void 0, fun
|
|
|
43
43
|
yield model.hooks[type](params);
|
|
44
44
|
}
|
|
45
45
|
if (model.events[type]) {
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
// if (context.trx) {
|
|
54
|
-
// delete context.trx;
|
|
55
|
-
// }
|
|
46
|
+
// Developers shouldn't be able to access transaction in events. Because
|
|
47
|
+
// we don't await for the events. If the developer uses the transaction and
|
|
48
|
+
// try to commit something, it would be lost cause the transaction could be
|
|
49
|
+
// already completed.
|
|
50
|
+
const database = (yield Services_1.IoCService.use("Database"));
|
|
51
|
+
params.database = database;
|
|
52
|
+
// Calling the events
|
|
56
53
|
model.events[type](params);
|
|
57
54
|
}
|
|
58
55
|
});
|
|
@@ -149,7 +146,7 @@ const getRelatedData = (data, withArray, model, modelList, database, handler, re
|
|
|
149
146
|
for (const clientQuery of withArray) {
|
|
150
147
|
// Find the relation of the model. If the model doesn't have any relationship like the
|
|
151
148
|
// user wants, we can't show anything.
|
|
152
|
-
const definedRelation = model.relations.find((relation) => relation.
|
|
149
|
+
const definedRelation = model.relations.find((relation) => relation.name === clientQuery.relationship);
|
|
153
150
|
if (!definedRelation) {
|
|
154
151
|
throw new ApiError_1.default(`Undefined relation: ${clientQuery.relationship}`);
|
|
155
152
|
}
|
|
@@ -194,6 +191,15 @@ const getRelatedData = (data, withArray, model, modelList, database, handler, re
|
|
|
194
191
|
// };
|
|
195
192
|
// }),
|
|
196
193
|
// ];
|
|
194
|
+
console.log([
|
|
195
|
+
...workList.map((relationship) => {
|
|
196
|
+
return {
|
|
197
|
+
relationship,
|
|
198
|
+
fields: [],
|
|
199
|
+
children: [],
|
|
200
|
+
};
|
|
201
|
+
}),
|
|
202
|
+
]);
|
|
197
203
|
// We should check if the column is defined on the table.
|
|
198
204
|
const undefinedColumns = selectColumns.filter((column) => !foreignModel.columnNames.includes(column));
|
|
199
205
|
if (undefinedColumns.length > 0) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Knex } from "knex";
|
|
2
|
-
import { Request, Response, NextFunction } from "express";
|
|
2
|
+
import { Express, Request, Response, NextFunction } from "express";
|
|
3
3
|
import { Column } from "knex-schema-inspector/lib/types/column";
|
|
4
4
|
import { HandlerTypes, LogLevels, HttpMethods, HookFunctionTypes, Extensions, Relationships, SortTypes, ConditionTypes, DependencyTypes } from "./Enums";
|
|
5
5
|
import Model from "./Model";
|
|
@@ -21,7 +21,7 @@ interface IPoolConfig {
|
|
|
21
21
|
interface IMigrationConfig {
|
|
22
22
|
tableName: "knex_migrations";
|
|
23
23
|
}
|
|
24
|
-
interface IHandlerBasedTransactionConfig {
|
|
24
|
+
export interface IHandlerBasedTransactionConfig {
|
|
25
25
|
handler: HandlerTypes | HandlerTypes[];
|
|
26
26
|
transaction: boolean;
|
|
27
27
|
}
|
|
@@ -33,6 +33,7 @@ export interface IApplicationConfig extends IConfig {
|
|
|
33
33
|
env: string;
|
|
34
34
|
port: number;
|
|
35
35
|
logLevel: LogLevels;
|
|
36
|
+
prefix: string;
|
|
36
37
|
transaction: boolean | IHandlerBasedTransactionConfig | IHandlerBasedTransactionConfig[];
|
|
37
38
|
serializers: ((data: any, request: Request) => void)[] | IHandlerBasedSerializer[];
|
|
38
39
|
}
|
|
@@ -51,12 +52,12 @@ export interface IFolders {
|
|
|
51
52
|
Models: string;
|
|
52
53
|
}
|
|
53
54
|
export interface IGeneralHooks {
|
|
54
|
-
onBeforeInit: () => void | null;
|
|
55
|
-
onAfterInit: () => void | null;
|
|
55
|
+
onBeforeInit: (app: Express) => void | null;
|
|
56
|
+
onAfterInit: (app: Express) => void | null;
|
|
56
57
|
}
|
|
57
58
|
export interface IHandlerBaseMiddleware {
|
|
58
59
|
handler: HandlerTypes[];
|
|
59
|
-
middleware: (req: Request, res: Response, next: NextFunction) => void
|
|
60
|
+
middleware: (req: Request, res: Response, next: NextFunction) => void | Promise<void>;
|
|
60
61
|
}
|
|
61
62
|
export interface IHookParameter {
|
|
62
63
|
req: Request;
|
|
@@ -65,7 +66,7 @@ export interface IHookParameter {
|
|
|
65
66
|
model: IModelService;
|
|
66
67
|
parentModel: IModelService | null;
|
|
67
68
|
relation: IRelation | null;
|
|
68
|
-
database: Knex;
|
|
69
|
+
database: Knex | Knex.Transaction;
|
|
69
70
|
conditions: IQuery | null;
|
|
70
71
|
query: Knex.QueryBuilder | null;
|
|
71
72
|
result: any | null;
|
|
@@ -108,7 +109,7 @@ export interface IRequestPack {
|
|
|
108
109
|
model: IModelService;
|
|
109
110
|
parentModel: IModelService | null;
|
|
110
111
|
relation: IRelation | null;
|
|
111
|
-
database: Knex;
|
|
112
|
+
database: Knex | Knex.Transaction;
|
|
112
113
|
}
|
|
113
114
|
export interface IRouteDocumentation {
|
|
114
115
|
model: string;
|
package/build/src/Model.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from "express";
|
|
2
|
-
import { IRelation, IMethodBaseConfig, IMethodBaseValidations, IHandlerBaseMiddleware } from "./Interfaces";
|
|
2
|
+
import { IRelation, IMethodBaseConfig, IMethodBaseValidations, IHandlerBaseMiddleware, IHandlerBasedTransactionConfig } from "./Interfaces";
|
|
3
3
|
import { HandlerTypes, HttpMethods } from "./Enums";
|
|
4
4
|
declare class Model {
|
|
5
5
|
constructor();
|
|
@@ -12,7 +12,7 @@ declare class Model {
|
|
|
12
12
|
get hiddens(): string[];
|
|
13
13
|
get createdAtColumn(): string | null;
|
|
14
14
|
get updatedAtColumn(): string | null;
|
|
15
|
-
get transaction():
|
|
15
|
+
get transaction(): boolean | IHandlerBasedTransactionConfig | IHandlerBasedTransactionConfig[] | null;
|
|
16
16
|
get ignore(): boolean;
|
|
17
17
|
getFillableFields(methodType: HttpMethods): string[];
|
|
18
18
|
getValidationRules(methodType: HttpMethods): Record<string, string> | null;
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
26
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
27
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -18,10 +41,14 @@ class FileResolver {
|
|
|
18
41
|
resolve(directory) {
|
|
19
42
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
43
|
const results = {};
|
|
21
|
-
const files = yield fs_1.default.readdirSync(directory)
|
|
44
|
+
const files = (yield fs_1.default.readdirSync(directory, { withFileTypes: true }))
|
|
45
|
+
.filter((item) => !item.isDirectory())
|
|
46
|
+
.filter((filename) => filename.name.includes(".ts"))
|
|
47
|
+
.filter((filename) => filename.name.includes(".ts"))
|
|
48
|
+
.map((item) => item.name);
|
|
22
49
|
for (const file of files) {
|
|
23
50
|
const configFile = path_1.default.join(directory, file);
|
|
24
|
-
const { default: content } = require(configFile);
|
|
51
|
+
const { default: content } = yield Promise.resolve().then(() => __importStar(require(configFile)));
|
|
25
52
|
const key = file.replace(".ts", "");
|
|
26
53
|
if (content === null || content === void 0 ? void 0 : content.prototype) {
|
|
27
54
|
results[key] = new content();
|
|
@@ -42,7 +69,7 @@ class FileResolver {
|
|
|
42
69
|
.map((item) => item.name);
|
|
43
70
|
for (const file of files) {
|
|
44
71
|
const configFile = path_1.default.join(directory, file);
|
|
45
|
-
const content = require(configFile);
|
|
72
|
+
const content = yield Promise.resolve().then(() => __importStar(require(configFile)));
|
|
46
73
|
const key = file.replace(".ts", "");
|
|
47
74
|
results[key] = content;
|
|
48
75
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const Services_1 = require("../Services");
|
|
17
|
+
const _1 = require(".");
|
|
18
|
+
class GeneralHookResolver {
|
|
19
|
+
static resolve() {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
const folders = (yield Services_1.IoCService.use("Folders"));
|
|
22
|
+
const fileResolver = new _1.FileResolver();
|
|
23
|
+
const content = yield fileResolver.resolveContent(path_1.default.join(folders.App, "app"));
|
|
24
|
+
if (content && content.init) {
|
|
25
|
+
const { onBeforeInit = null, onAfterInit = null } = content.init;
|
|
26
|
+
return { onBeforeInit, onAfterInit };
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
onBeforeInit: null,
|
|
30
|
+
onAfterInit: null,
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.default = GeneralHookResolver;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from "express";
|
|
2
|
+
import { IModelService } from "../Interfaces";
|
|
3
|
+
import { HandlerTypes } from "../Enums";
|
|
4
|
+
declare class ModelMiddlewareResolver {
|
|
5
|
+
static resolve(model: IModelService, handlerType: HandlerTypes): ((req: Request, res: Response, next: NextFunction) => void)[];
|
|
6
|
+
}
|
|
7
|
+
export default ModelMiddlewareResolver;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
class ModelMiddlewareResolver {
|
|
4
|
+
static resolve(model, handlerType) {
|
|
5
|
+
const results = [];
|
|
6
|
+
const middlewares = model.instance.middlewares;
|
|
7
|
+
if (Array.isArray(middlewares)) {
|
|
8
|
+
middlewares.forEach((item) => {
|
|
9
|
+
if (item === null || item === void 0 ? void 0 : item.handler) {
|
|
10
|
+
const methodBasedMiddlewares = item;
|
|
11
|
+
if (methodBasedMiddlewares.handler.includes(handlerType)) {
|
|
12
|
+
results.push(methodBasedMiddlewares.middleware);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
results.push(item);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const methodBasedMiddlewares = middlewares;
|
|
22
|
+
if (methodBasedMiddlewares.handler.includes(handlerType)) {
|
|
23
|
+
results.push(methodBasedMiddlewares.middleware);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return results;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.default = ModelMiddlewareResolver;
|
|
@@ -35,7 +35,13 @@ class ModelResolver {
|
|
|
35
35
|
for (const model of modelList.get()) {
|
|
36
36
|
const relationMethods = this.getInstanceMethods(model);
|
|
37
37
|
for (const relationMethod of relationMethods) {
|
|
38
|
-
model.
|
|
38
|
+
const relationFunction = model.instance[relationMethod];
|
|
39
|
+
if (typeof relationFunction !== "function") {
|
|
40
|
+
throw new Error(`Model relation definition should be a function: ${model.name}.${relationMethod}`);
|
|
41
|
+
}
|
|
42
|
+
const definition = relationFunction.call(model.instance);
|
|
43
|
+
definition.name = relationMethod;
|
|
44
|
+
model.relations.push(definition);
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IModelService } from "../Interfaces";
|
|
2
|
+
import { HandlerTypes } from "../Enums";
|
|
3
|
+
declare class TransactionResolver {
|
|
4
|
+
static resolve(model: IModelService, handlerType: HandlerTypes): Promise<boolean>;
|
|
5
|
+
private static resolveTransactionOption;
|
|
6
|
+
private static getTransactionConfiguration;
|
|
7
|
+
}
|
|
8
|
+
export default TransactionResolver;
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
class TransactionResolver {
|
|
14
|
+
static resolve(model, handlerType) {
|
|
15
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
16
|
+
const config = yield Services_1.IoCService.use("Config");
|
|
17
|
+
const global = config.Application.transaction;
|
|
18
|
+
const local = model.instance.transaction;
|
|
19
|
+
let privilegedOption = false;
|
|
20
|
+
if (global) {
|
|
21
|
+
privilegedOption = TransactionResolver.resolveTransactionOption(global, handlerType, privilegedOption);
|
|
22
|
+
}
|
|
23
|
+
if (local !== null) {
|
|
24
|
+
privilegedOption = TransactionResolver.resolveTransactionOption(local, handlerType, privilegedOption);
|
|
25
|
+
}
|
|
26
|
+
return privilegedOption;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
static getTransactionConfiguration(configItem, handlerType) {
|
|
30
|
+
// An item definitions might have handler array such as;
|
|
31
|
+
//
|
|
32
|
+
// handler: [HandlerTypes.ALL, HandlerTypes.INSERT]
|
|
33
|
+
if (Array.isArray(configItem.handler)) {
|
|
34
|
+
// If this is an array, we should find the matched handler type
|
|
35
|
+
const found = configItem.handler.find((item) => item === handlerType);
|
|
36
|
+
// If there is, this is the our transaction choice
|
|
37
|
+
if (found) {
|
|
38
|
+
return configItem.transaction;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (configItem.handler === handlerType) {
|
|
42
|
+
// If the "configItem.handler" is not an array, should be matched
|
|
43
|
+
// with the handlerType. If it matches, it means that this is our
|
|
44
|
+
// transaction configuration
|
|
45
|
+
return configItem.transaction;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
TransactionResolver.resolveTransactionOption = (option, handlerType, defaultValue) => {
|
|
51
|
+
if (Array.isArray(option)) {
|
|
52
|
+
// If this is an array, we should treat it like an array.
|
|
53
|
+
const configs = option;
|
|
54
|
+
// We should check every item of the array
|
|
55
|
+
for (const configItem of configs) {
|
|
56
|
+
const value = TransactionResolver.getTransactionConfiguration(configItem, handlerType);
|
|
57
|
+
if (value) {
|
|
58
|
+
defaultValue = configItem.transaction;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (typeof option === "boolean") {
|
|
63
|
+
// Developer should be able to select a boolean value for all kind of routes
|
|
64
|
+
defaultValue = option;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const configItem = option;
|
|
68
|
+
const value = TransactionResolver.getTransactionConfiguration(configItem, handlerType);
|
|
69
|
+
if (value) {
|
|
70
|
+
defaultValue = configItem.transaction;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return defaultValue;
|
|
74
|
+
};
|
|
75
|
+
exports.default = TransactionResolver;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IModelService, IWith } from "../Interfaces";
|
|
2
|
+
declare class WithQueryResolver {
|
|
3
|
+
model: IModelService;
|
|
4
|
+
models: IModelService[];
|
|
5
|
+
constructor(model: IModelService, models: IModelService[]);
|
|
6
|
+
resolve(expression: string): IWith[];
|
|
7
|
+
private resolveRelationsByKey;
|
|
8
|
+
private toNestedArray;
|
|
9
|
+
private getKey;
|
|
10
|
+
private getGroupValue;
|
|
11
|
+
private splitByGroups;
|
|
12
|
+
}
|
|
13
|
+
export default WithQueryResolver;
|
|
@@ -0,0 +1,116 @@
|
|
|
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
|
+
const ApiError_1 = __importDefault(require("../Exceptions/ApiError"));
|
|
7
|
+
class WithQueryResolver {
|
|
8
|
+
constructor(model, models) {
|
|
9
|
+
this.getKey = (group) => {
|
|
10
|
+
const firstIndex = group.indexOf("{");
|
|
11
|
+
if (firstIndex > -1) {
|
|
12
|
+
return group.substring(0, firstIndex);
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
};
|
|
16
|
+
this.getGroupValue = (group) => {
|
|
17
|
+
const firstIndex = group.indexOf("{");
|
|
18
|
+
if (firstIndex > -1) {
|
|
19
|
+
return group.substring(firstIndex + 1, group.length - 1);
|
|
20
|
+
}
|
|
21
|
+
return group;
|
|
22
|
+
};
|
|
23
|
+
this.model = model;
|
|
24
|
+
this.models = models;
|
|
25
|
+
}
|
|
26
|
+
resolve(expression) {
|
|
27
|
+
const result = [];
|
|
28
|
+
const root = {
|
|
29
|
+
key: "root",
|
|
30
|
+
children: [],
|
|
31
|
+
};
|
|
32
|
+
const currentModel = this.model;
|
|
33
|
+
this.toNestedArray(root, expression);
|
|
34
|
+
this.resolveRelationsByKey(result, null, root.children, currentModel);
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
resolveRelationsByKey(result, fields, expressions, model) {
|
|
38
|
+
for (const expression of expressions) {
|
|
39
|
+
if (expression.key.trim().length === 0) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const relationFunction = model.instance[expression.key];
|
|
43
|
+
if (typeof relationFunction === "function") {
|
|
44
|
+
const definition = relationFunction.call(model.instance);
|
|
45
|
+
const relationModel = this.models.find((item) => item.name === definition.model);
|
|
46
|
+
if (relationModel === undefined) {
|
|
47
|
+
throw new ApiError_1.default(`Undefined relation model: ${definition.model} (${expression.key})`);
|
|
48
|
+
}
|
|
49
|
+
const data = {
|
|
50
|
+
relationship: expression.key,
|
|
51
|
+
relationModel,
|
|
52
|
+
fields: [],
|
|
53
|
+
children: [],
|
|
54
|
+
};
|
|
55
|
+
if (expression.children.length > 0) {
|
|
56
|
+
this.resolveRelationsByKey(data.children, data.fields, expression.children, relationModel);
|
|
57
|
+
}
|
|
58
|
+
result.push(data);
|
|
59
|
+
}
|
|
60
|
+
else if (fields !== null) {
|
|
61
|
+
if (model.columnNames.includes(expression.key)) {
|
|
62
|
+
fields.push(expression.key);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
throw new ApiError_1.default(`It is not a field or a relation: ${expression.key}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
throw new ApiError_1.default(`Unknown expression: ${expression.key}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
toNestedArray(root, expression) {
|
|
74
|
+
const groups = this.splitByGroups(expression);
|
|
75
|
+
for (const group of groups) {
|
|
76
|
+
const key = this.getKey(group);
|
|
77
|
+
if (key) {
|
|
78
|
+
const child = {
|
|
79
|
+
key,
|
|
80
|
+
children: [],
|
|
81
|
+
};
|
|
82
|
+
this.toNestedArray(child, this.getGroupValue(group));
|
|
83
|
+
root.children.push(child);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
root.children.push(...group.split("|").map((field) => {
|
|
87
|
+
return {
|
|
88
|
+
key: field,
|
|
89
|
+
children: [],
|
|
90
|
+
};
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
splitByGroups(expression) {
|
|
96
|
+
const result = [];
|
|
97
|
+
let bracket = 0;
|
|
98
|
+
let startedAt = 0;
|
|
99
|
+
for (let index = 0; index < expression.length; index++) {
|
|
100
|
+
const char = expression[index];
|
|
101
|
+
if (char === "{") {
|
|
102
|
+
bracket++;
|
|
103
|
+
}
|
|
104
|
+
else if (char === "}") {
|
|
105
|
+
bracket--;
|
|
106
|
+
}
|
|
107
|
+
if (bracket === 0 && char === ",") {
|
|
108
|
+
result.push(expression.substring(startedAt, index));
|
|
109
|
+
startedAt = index + 1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
result.push(expression.substring(startedAt, expression.length));
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.default = WithQueryResolver;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import FileResolver from "./FileResolver";
|
|
2
2
|
import FolderResolver from "./FolderResolver";
|
|
3
|
+
import GeneralHookResolver from "./GeneralHookResolver";
|
|
4
|
+
import ModelMiddlewareResolver from "./ModelMiddlewareResolver";
|
|
3
5
|
import ModelResolver from "./ModelResolver";
|
|
4
|
-
|
|
6
|
+
import TransactionResolver from "./TransactionResolver";
|
|
7
|
+
import WithQueryResolver from "./WithQueryResolver";
|
|
8
|
+
export { FileResolver, FolderResolver, GeneralHookResolver, ModelMiddlewareResolver, ModelResolver, TransactionResolver, WithQueryResolver, };
|
|
@@ -3,10 +3,18 @@ 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.ModelResolver = exports.FolderResolver = exports.FileResolver = void 0;
|
|
6
|
+
exports.WithQueryResolver = exports.TransactionResolver = exports.ModelResolver = exports.ModelMiddlewareResolver = exports.GeneralHookResolver = exports.FolderResolver = exports.FileResolver = void 0;
|
|
7
7
|
const FileResolver_1 = __importDefault(require("./FileResolver"));
|
|
8
8
|
exports.FileResolver = FileResolver_1.default;
|
|
9
9
|
const FolderResolver_1 = __importDefault(require("./FolderResolver"));
|
|
10
10
|
exports.FolderResolver = FolderResolver_1.default;
|
|
11
|
+
const GeneralHookResolver_1 = __importDefault(require("./GeneralHookResolver"));
|
|
12
|
+
exports.GeneralHookResolver = GeneralHookResolver_1.default;
|
|
13
|
+
const ModelMiddlewareResolver_1 = __importDefault(require("./ModelMiddlewareResolver"));
|
|
14
|
+
exports.ModelMiddlewareResolver = ModelMiddlewareResolver_1.default;
|
|
11
15
|
const ModelResolver_1 = __importDefault(require("./ModelResolver"));
|
|
12
16
|
exports.ModelResolver = ModelResolver_1.default;
|
|
17
|
+
const TransactionResolver_1 = __importDefault(require("./TransactionResolver"));
|
|
18
|
+
exports.TransactionResolver = TransactionResolver_1.default;
|
|
19
|
+
const WithQueryResolver_1 = __importDefault(require("./WithQueryResolver"));
|
|
20
|
+
exports.WithQueryResolver = WithQueryResolver_1.default;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { IRawQuery, IQuery, ISortField, NestedWhere, IWhere, IWith, IModelService } from "../Interfaces";
|
|
1
|
+
import { IQuery, ISortField, NestedWhere, IWhere, IModelService } from "../Interfaces";
|
|
3
2
|
import { Knex } from "knex";
|
|
4
3
|
declare class QueryService {
|
|
5
4
|
model: IModelService;
|
|
@@ -13,27 +12,23 @@ declare class QueryService {
|
|
|
13
12
|
applyWheresInsideGroup(sub: Knex.QueryBuilder, ruleSet: NestedWhere | IWhere): void;
|
|
14
13
|
applyWheres(query: Knex.QueryBuilder, ruleSet: NestedWhere): void;
|
|
15
14
|
get(query: any): IQuery;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
_addRelationColumns(withs: IWith[]): void;
|
|
35
|
-
_getConditionMethodName(ruleSet: IWhere): "orWhere" | "where";
|
|
36
|
-
_hasSpecialStructure(field: string, structure: string): boolean;
|
|
37
|
-
_shouldBeAcceptableColumn(field: string): void;
|
|
15
|
+
private getUsedColumns;
|
|
16
|
+
private applyConditionRule;
|
|
17
|
+
private applyRelatedQueryJoins;
|
|
18
|
+
private applyJoinIfNecessary;
|
|
19
|
+
private addJoinOnce;
|
|
20
|
+
private parseSections;
|
|
21
|
+
private parsePage;
|
|
22
|
+
private parsePerPage;
|
|
23
|
+
private parseFields;
|
|
24
|
+
private parseSortingOptions;
|
|
25
|
+
private parseConditions;
|
|
26
|
+
private parseCondition;
|
|
27
|
+
private parseConditionObject;
|
|
28
|
+
private applySpecialCondition;
|
|
29
|
+
private addRelationColumns;
|
|
30
|
+
private getConditionMethodName;
|
|
31
|
+
private hasSpecialStructure;
|
|
32
|
+
private shouldBeAcceptableColumn;
|
|
38
33
|
}
|
|
39
34
|
export default QueryService;
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const Enums_1 = require("../Enums");
|
|
7
7
|
const ApiError_1 = __importDefault(require("../Exceptions/ApiError"));
|
|
8
|
+
const Resolvers_1 = require("../Resolvers");
|
|
8
9
|
class QueryService {
|
|
9
10
|
constructor(model, models) {
|
|
10
11
|
this.model = model;
|
|
@@ -15,7 +16,7 @@ class QueryService {
|
|
|
15
16
|
}
|
|
16
17
|
applyFields(query, fields) {
|
|
17
18
|
// Users should be able to select some fields to show.
|
|
18
|
-
if (fields.length === 1 && fields[0] === "*") {
|
|
19
|
+
if (fields.length === 0 || (fields.length === 1 && fields[0] === "*")) {
|
|
19
20
|
query.select(`${this.model.instance.table}.*`);
|
|
20
21
|
}
|
|
21
22
|
else {
|
|
@@ -46,7 +47,7 @@ class QueryService {
|
|
|
46
47
|
// If the item is not an array, it means that it is a standard condition
|
|
47
48
|
if (Array.isArray(item) === false) {
|
|
48
49
|
const condition = item;
|
|
49
|
-
this.
|
|
50
|
+
this.applyConditionRule(sub, condition);
|
|
50
51
|
}
|
|
51
52
|
else {
|
|
52
53
|
// If the item is an array, we should create the query recursively.
|
|
@@ -66,18 +67,18 @@ class QueryService {
|
|
|
66
67
|
}
|
|
67
68
|
else {
|
|
68
69
|
const condition = ruleSet;
|
|
69
|
-
this.
|
|
70
|
+
this.applyConditionRule(sub, condition);
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
applyWheres(query, ruleSet) {
|
|
73
74
|
query.where((sub) => {
|
|
74
75
|
this.applyWheresInsideGroup(sub, ruleSet);
|
|
75
76
|
});
|
|
76
|
-
this.
|
|
77
|
+
this.applyRelatedQueryJoins(query, ruleSet);
|
|
77
78
|
}
|
|
78
79
|
get(query) {
|
|
79
|
-
const conditions = this.
|
|
80
|
-
const usedColumns = this.
|
|
80
|
+
const conditions = this.parseSections(query);
|
|
81
|
+
const usedColumns = this.getUsedColumns(conditions);
|
|
81
82
|
const undefinedColumns = usedColumns.filter((columnName) => {
|
|
82
83
|
let currentModel = this.model;
|
|
83
84
|
let realColumName = columnName;
|
|
@@ -93,15 +94,15 @@ class QueryService {
|
|
|
93
94
|
}
|
|
94
95
|
return conditions;
|
|
95
96
|
}
|
|
96
|
-
|
|
97
|
+
getUsedColumns(conditions) {
|
|
97
98
|
return [
|
|
98
99
|
...conditions.fields,
|
|
99
100
|
...conditions.sort.map((item) => item.name),
|
|
100
101
|
...Array.from(this.usedConditionColumns),
|
|
101
102
|
];
|
|
102
103
|
}
|
|
103
|
-
|
|
104
|
-
const method = this.
|
|
104
|
+
applyConditionRule(sub, ruleSet) {
|
|
105
|
+
const method = this.getConditionMethodName(ruleSet);
|
|
105
106
|
const zeroArguments = ["Null", "NotNull"];
|
|
106
107
|
const oneArguments = ["In", "NotIn", "Between", "NotBetween"];
|
|
107
108
|
const fullFieldPath = `${ruleSet.table}.${ruleSet.field}`;
|
|
@@ -115,7 +116,7 @@ class QueryService {
|
|
|
115
116
|
}
|
|
116
117
|
return sub[method](fullFieldPath, ruleSet.condition, ruleSet.value);
|
|
117
118
|
}
|
|
118
|
-
|
|
119
|
+
applyRelatedQueryJoins(query, ruleSet) {
|
|
119
120
|
if (!ruleSet) {
|
|
120
121
|
return;
|
|
121
122
|
}
|
|
@@ -124,23 +125,23 @@ class QueryService {
|
|
|
124
125
|
// If the item is not an array, it means that it is a standard condition
|
|
125
126
|
if (Array.isArray(item) === false) {
|
|
126
127
|
const condition = item;
|
|
127
|
-
this.
|
|
128
|
+
this.applyJoinIfNecessary(query, condition);
|
|
128
129
|
}
|
|
129
130
|
else {
|
|
130
|
-
this.
|
|
131
|
+
this.applyRelatedQueryJoins(query, item);
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
}
|
|
134
135
|
else {
|
|
135
|
-
this.
|
|
136
|
+
this.applyJoinIfNecessary(query, ruleSet);
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
|
-
|
|
139
|
+
applyJoinIfNecessary(query, ruleSet) {
|
|
139
140
|
if (ruleSet.table !== this.model.instance.table) {
|
|
140
|
-
this.
|
|
141
|
+
this.addJoinOnce(query, ruleSet);
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
|
-
|
|
144
|
+
addJoinOnce(query, ruleSet) {
|
|
144
145
|
const { model, relation } = ruleSet;
|
|
145
146
|
if (!relation) {
|
|
146
147
|
return;
|
|
@@ -154,21 +155,7 @@ class QueryService {
|
|
|
154
155
|
query.leftJoin(tableName, primaryKey, foreignKey);
|
|
155
156
|
this.createdJoins.push(relation.name);
|
|
156
157
|
}
|
|
157
|
-
|
|
158
|
-
if (typeof query !== "object") {
|
|
159
|
-
throw new ApiError_1.default("You have to send an object to get sections.");
|
|
160
|
-
}
|
|
161
|
-
const sections = {
|
|
162
|
-
q: query.q || null,
|
|
163
|
-
page: query.page || 1,
|
|
164
|
-
per_page: query.per_page || 10,
|
|
165
|
-
sort: query.sory || null,
|
|
166
|
-
fields: query.fields || null,
|
|
167
|
-
with: query.with || null,
|
|
168
|
-
};
|
|
169
|
-
return sections;
|
|
170
|
-
}
|
|
171
|
-
_parseSections(sections) {
|
|
158
|
+
parseSections(sections) {
|
|
172
159
|
if (sections.q) {
|
|
173
160
|
const queryContent = sections.q.replace(/%20/g, "").replace(/ /g, "");
|
|
174
161
|
// Users can send an unacceptable query string. We shouldn't allow them to
|
|
@@ -180,18 +167,19 @@ class QueryService {
|
|
|
180
167
|
throw new ApiError_1.default(`Unacceptable query string: ${queryContent}`);
|
|
181
168
|
}
|
|
182
169
|
}
|
|
170
|
+
const withQueryResolver = new Resolvers_1.WithQueryResolver(this.model, this.models);
|
|
183
171
|
const query = {
|
|
184
|
-
page: this.
|
|
185
|
-
per_page: this.
|
|
186
|
-
fields: this.
|
|
187
|
-
sort: this.
|
|
188
|
-
q: this.
|
|
189
|
-
with:
|
|
172
|
+
page: this.parsePage(sections.page),
|
|
173
|
+
per_page: this.parsePerPage(sections.per_page),
|
|
174
|
+
fields: this.parseFields(sections.fields),
|
|
175
|
+
sort: this.parseSortingOptions(sections.sort),
|
|
176
|
+
q: this.parseCondition(sections.q),
|
|
177
|
+
with: withQueryResolver.resolve((sections === null || sections === void 0 ? void 0 : sections.with) || ""),
|
|
190
178
|
};
|
|
191
|
-
this.
|
|
179
|
+
this.addRelationColumns(query.with);
|
|
192
180
|
return query;
|
|
193
181
|
}
|
|
194
|
-
|
|
182
|
+
parsePage(content) {
|
|
195
183
|
const value = parseInt(content);
|
|
196
184
|
if (isNaN(value)) {
|
|
197
185
|
return 1;
|
|
@@ -201,14 +189,14 @@ class QueryService {
|
|
|
201
189
|
}
|
|
202
190
|
return value;
|
|
203
191
|
}
|
|
204
|
-
|
|
192
|
+
parsePerPage(content) {
|
|
205
193
|
const value = parseInt(content);
|
|
206
194
|
if (isNaN(value) || value <= 1 || value > 10000) {
|
|
207
195
|
return 10;
|
|
208
196
|
}
|
|
209
197
|
return value;
|
|
210
198
|
}
|
|
211
|
-
|
|
199
|
+
parseFields(content) {
|
|
212
200
|
if (!content) {
|
|
213
201
|
return [];
|
|
214
202
|
}
|
|
@@ -219,11 +207,11 @@ class QueryService {
|
|
|
219
207
|
}
|
|
220
208
|
const fields = strContent.split(",");
|
|
221
209
|
fields.forEach((field) => {
|
|
222
|
-
this.
|
|
210
|
+
this.shouldBeAcceptableColumn(field);
|
|
223
211
|
});
|
|
224
212
|
return fields;
|
|
225
213
|
}
|
|
226
|
-
|
|
214
|
+
parseSortingOptions(content) {
|
|
227
215
|
// If there is not any sorting options, we don't have to return any value
|
|
228
216
|
if (!content) {
|
|
229
217
|
return [];
|
|
@@ -239,7 +227,7 @@ class QueryService {
|
|
|
239
227
|
if (field.indexOf("+") === 0) {
|
|
240
228
|
field = field.substr(1);
|
|
241
229
|
}
|
|
242
|
-
this.
|
|
230
|
+
this.shouldBeAcceptableColumn(field);
|
|
243
231
|
result.push({
|
|
244
232
|
name: field,
|
|
245
233
|
type,
|
|
@@ -247,28 +235,28 @@ class QueryService {
|
|
|
247
235
|
}
|
|
248
236
|
return result;
|
|
249
237
|
}
|
|
250
|
-
|
|
238
|
+
parseConditions(conditions) {
|
|
251
239
|
if (!Array.isArray(conditions)) {
|
|
252
240
|
throw new Error("An array should be sent to parseConditions() method.");
|
|
253
241
|
}
|
|
254
242
|
return conditions.map((condition) => {
|
|
255
|
-
return this.
|
|
243
|
+
return this.parseCondition(condition);
|
|
256
244
|
});
|
|
257
245
|
}
|
|
258
|
-
|
|
246
|
+
parseCondition(content) {
|
|
259
247
|
if (Array.isArray(content)) {
|
|
260
|
-
return this.
|
|
248
|
+
return this.parseConditions(content);
|
|
261
249
|
}
|
|
262
250
|
if (!content) {
|
|
263
251
|
return [];
|
|
264
252
|
}
|
|
265
253
|
const wheres = [];
|
|
266
254
|
for (const key in content) {
|
|
267
|
-
wheres.push(this.
|
|
255
|
+
wheres.push(this.parseConditionObject(content, key));
|
|
268
256
|
}
|
|
269
257
|
return wheres;
|
|
270
258
|
}
|
|
271
|
-
|
|
259
|
+
parseConditionObject(content, key) {
|
|
272
260
|
const where = {
|
|
273
261
|
prefix: null,
|
|
274
262
|
model: this.model,
|
|
@@ -290,7 +278,7 @@ class QueryService {
|
|
|
290
278
|
// If there is not any value, it means that we should check nullable values
|
|
291
279
|
if (where.value === null) {
|
|
292
280
|
// If the client wants to see not nullable values
|
|
293
|
-
if (this.
|
|
281
|
+
if (this.hasSpecialStructure(where.field, ".$not")) {
|
|
294
282
|
where.field = where.field.replace(".$not", "");
|
|
295
283
|
where.condition = Enums_1.ConditionTypes.NotNull;
|
|
296
284
|
}
|
|
@@ -301,17 +289,17 @@ class QueryService {
|
|
|
301
289
|
}
|
|
302
290
|
else {
|
|
303
291
|
// If there is value, we should check it
|
|
304
|
-
this.
|
|
305
|
-
this.
|
|
306
|
-
this.
|
|
307
|
-
this.
|
|
308
|
-
this.
|
|
309
|
-
this.
|
|
310
|
-
this.
|
|
311
|
-
this.
|
|
312
|
-
this.
|
|
313
|
-
this.
|
|
314
|
-
this.
|
|
292
|
+
this.applySpecialCondition(where, "$not", Enums_1.ConditionTypes["<>"]);
|
|
293
|
+
this.applySpecialCondition(where, "$gt", Enums_1.ConditionTypes[">"]);
|
|
294
|
+
this.applySpecialCondition(where, "$gte", Enums_1.ConditionTypes[">="]);
|
|
295
|
+
this.applySpecialCondition(where, "$lt", Enums_1.ConditionTypes["<"]);
|
|
296
|
+
this.applySpecialCondition(where, "$lte", Enums_1.ConditionTypes["<="]);
|
|
297
|
+
this.applySpecialCondition(where, "$like", Enums_1.ConditionTypes.LIKE);
|
|
298
|
+
this.applySpecialCondition(where, "$notLike", Enums_1.ConditionTypes["NOT LIKE"]);
|
|
299
|
+
this.applySpecialCondition(where, "$in", Enums_1.ConditionTypes.In);
|
|
300
|
+
this.applySpecialCondition(where, "$notIn", Enums_1.ConditionTypes.NotIn);
|
|
301
|
+
this.applySpecialCondition(where, "$between", Enums_1.ConditionTypes.Between);
|
|
302
|
+
this.applySpecialCondition(where, "$notBetween", Enums_1.ConditionTypes.NotBetween);
|
|
315
303
|
}
|
|
316
304
|
if (where.condition === "In" || where.condition === "NotIn") {
|
|
317
305
|
where.value = where.value.split(",");
|
|
@@ -338,94 +326,33 @@ class QueryService {
|
|
|
338
326
|
where.relation = relation;
|
|
339
327
|
where.field = field;
|
|
340
328
|
}
|
|
341
|
-
this.
|
|
329
|
+
this.shouldBeAcceptableColumn(where.field);
|
|
342
330
|
this.usedConditionColumns.push(`${where.table}.${where.field}`);
|
|
343
331
|
return where;
|
|
344
332
|
}
|
|
345
|
-
|
|
346
|
-
if (!content) {
|
|
347
|
-
return [];
|
|
348
|
-
}
|
|
349
|
-
const stringContent = content;
|
|
350
|
-
return stringContent.split(",");
|
|
351
|
-
}
|
|
352
|
-
_parseWith(items) {
|
|
353
|
-
const result = [];
|
|
354
|
-
for (const item of items) {
|
|
355
|
-
let relationship = item;
|
|
356
|
-
let fields = [];
|
|
357
|
-
let children = [];
|
|
358
|
-
const columnIndex = relationship.indexOf("{");
|
|
359
|
-
if (columnIndex > -1) {
|
|
360
|
-
fields = this._splitWithRecursive(relationship.substr(columnIndex + 1, relationship.length - columnIndex - 2));
|
|
361
|
-
relationship = relationship.substr(0, columnIndex);
|
|
362
|
-
}
|
|
363
|
-
// We are checking there is any children
|
|
364
|
-
children = fields.filter((field) => field.indexOf("{") > -1 || field.indexOf(".") > -1);
|
|
365
|
-
// Field list shouldn't have any related table
|
|
366
|
-
fields = fields.filter((field) => field.indexOf("{") === -1 && field.indexOf(".") === -1);
|
|
367
|
-
// We should validate fields are correct.
|
|
368
|
-
fields.forEach((field) => {
|
|
369
|
-
this._shouldBeAcceptableColumn(field);
|
|
370
|
-
});
|
|
371
|
-
// We should calculate recursivly all of childre
|
|
372
|
-
children = this._parseWith(children);
|
|
373
|
-
const relationModel = this.models.find((item) => item.name.toLowerCase() === relationship.toLowerCase());
|
|
374
|
-
if (!relationModel) {
|
|
375
|
-
throw new ApiError_1.default(`Undefined relationship: ${relationship}`);
|
|
376
|
-
}
|
|
377
|
-
result.push({
|
|
378
|
-
relationship,
|
|
379
|
-
relationModel,
|
|
380
|
-
fields,
|
|
381
|
-
children,
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
return result;
|
|
385
|
-
}
|
|
386
|
-
_splitWithRecursive(content) {
|
|
387
|
-
const result = [];
|
|
388
|
-
let startAt = 0;
|
|
389
|
-
let subcounter = 0;
|
|
390
|
-
for (let position = 0; position < content.length; position++) {
|
|
391
|
-
const current = content[position];
|
|
392
|
-
if (current === "{") {
|
|
393
|
-
subcounter++;
|
|
394
|
-
}
|
|
395
|
-
if (current === "}") {
|
|
396
|
-
subcounter--;
|
|
397
|
-
}
|
|
398
|
-
if (current === "|" && subcounter === 0) {
|
|
399
|
-
result.push(content.substr(startAt, position - startAt));
|
|
400
|
-
startAt = position + 1;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
result.push(content.substr(startAt));
|
|
404
|
-
return result;
|
|
405
|
-
}
|
|
406
|
-
_applySpecialCondition(where, structure, condition) {
|
|
333
|
+
applySpecialCondition(where, structure, condition) {
|
|
407
334
|
structure = `.${structure}`;
|
|
408
|
-
if (this.
|
|
335
|
+
if (this.hasSpecialStructure(where.field, structure)) {
|
|
409
336
|
where.field = where.field.replace(structure, "");
|
|
410
337
|
where.condition = condition;
|
|
411
338
|
}
|
|
412
339
|
}
|
|
413
|
-
|
|
340
|
+
addRelationColumns(withs) {
|
|
414
341
|
withs.forEach((item) => {
|
|
415
|
-
const relation = this.model.relations.find((i) => i.
|
|
342
|
+
const relation = this.model.relations.find((i) => i.name === item.relationship);
|
|
416
343
|
if (!relation) {
|
|
417
344
|
throw new ApiError_1.default(`Undefined relation: ${item.relationship}`);
|
|
418
345
|
}
|
|
419
346
|
this.relationColumns.push(`${this.model.instance.table}.${relation.foreignKey}`);
|
|
420
347
|
});
|
|
421
348
|
}
|
|
422
|
-
|
|
349
|
+
getConditionMethodName(ruleSet) {
|
|
423
350
|
if (ruleSet.prefix === "or") {
|
|
424
351
|
return "orWhere";
|
|
425
352
|
}
|
|
426
353
|
return "where";
|
|
427
354
|
}
|
|
428
|
-
|
|
355
|
+
hasSpecialStructure(field, structure) {
|
|
429
356
|
if (field.indexOf(structure) === -1) {
|
|
430
357
|
return false;
|
|
431
358
|
}
|
|
@@ -434,7 +361,7 @@ class QueryService {
|
|
|
434
361
|
}
|
|
435
362
|
return false;
|
|
436
363
|
}
|
|
437
|
-
|
|
364
|
+
shouldBeAcceptableColumn(field) {
|
|
438
365
|
const regex = /^[0-9,a-z,A-Z_.]+$/;
|
|
439
366
|
if (!field.match(regex)) {
|
|
440
367
|
throw new ApiError_1.default(`Unacceptable field name: ${field}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "axe-api",
|
|
3
|
-
"version": "0.20.0-
|
|
3
|
+
"version": "0.20.0-rc21",
|
|
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",
|
|
@@ -25,9 +25,11 @@
|
|
|
25
25
|
"test:integration:postgres": "cd ./tests/integrations && node index.js postgres"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
+
"@types/express": "^4.17.13",
|
|
28
29
|
"change-case": "^4.1.2",
|
|
29
30
|
"dotenv": "^14.2.0",
|
|
30
31
|
"express": "^4.17.2",
|
|
32
|
+
"http-status-codes": "^2.2.0",
|
|
31
33
|
"knex": "^1.0.1",
|
|
32
34
|
"knex-paginate": "^3.0.0",
|
|
33
35
|
"knex-schema-inspector": "^1.7.1",
|
|
@@ -39,8 +41,10 @@
|
|
|
39
41
|
"@babel/core": "^7.16.10",
|
|
40
42
|
"@babel/node": "^7.16.8",
|
|
41
43
|
"@babel/preset-env": "^7.16.11",
|
|
44
|
+
"@babel/preset-typescript": "^7.18.6",
|
|
42
45
|
"@babel/runtime": "^7.16.7",
|
|
43
|
-
"
|
|
46
|
+
"@types/pluralize": "^0.0.29",
|
|
47
|
+
"babel-jest": "^29.0.3",
|
|
44
48
|
"babel-loader": "^8.2.3",
|
|
45
49
|
"babel-plugin-module-resolver": "^4.1.0",
|
|
46
50
|
"babel-preset-minify": "^0.5.1",
|
|
@@ -52,11 +56,11 @@
|
|
|
52
56
|
"eslint-plugin-standard": "^5.0.0",
|
|
53
57
|
"eslint-plugin-unicorn": "^33.0.1",
|
|
54
58
|
"eslint-watch": "^7.0.0",
|
|
55
|
-
"jest": "^27.4.7",
|
|
56
59
|
"mysql": "^2.18.1",
|
|
57
60
|
"nodemon": "^2.0.15",
|
|
58
61
|
"pg": "^8.7.1",
|
|
59
62
|
"set-value": ">=4.1.0",
|
|
60
|
-
"sqlite3": "^5.0.2"
|
|
63
|
+
"sqlite3": "^5.0.2",
|
|
64
|
+
"ts-jest": "^29.0.1"
|
|
61
65
|
}
|
|
62
66
|
}
|