axe-api 0.20.0-rc17 → 0.20.0-rc19
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/src/Builders/RouterBuilder.js +3 -2
- package/build/src/Handlers/Helpers.js +10 -1
- package/build/src/Interfaces.d.ts +3 -3
- package/build/src/Resolvers/ModelResolver.js +3 -1
- package/build/src/Resolvers/WithQueryResolver.d.ts +13 -0
- package/build/src/Resolvers/WithQueryResolver.js +113 -0
- package/build/src/Resolvers/index.d.ts +2 -1
- package/build/src/Resolvers/index.js +3 -1
- package/build/src/Services/QueryService.d.ts +19 -24
- package/build/src/Services/QueryService.js +57 -130
- package/package.json +5 -4
|
@@ -43,16 +43,17 @@ class RouterBuilder {
|
|
|
43
43
|
}
|
|
44
44
|
build() {
|
|
45
45
|
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
const app = yield Services_1.IoCService.useByType("App");
|
|
46
47
|
const logger = yield Services_1.IoCService.useByType("LogService");
|
|
47
48
|
const modelTree = yield Services_1.IoCService.useByType("ModelTree");
|
|
48
49
|
const modelList = yield Services_1.IoCService.useByType("ModelListService");
|
|
49
50
|
const generalHooks = yield Resolvers_1.GeneralHookResolver.resolve();
|
|
50
51
|
if (generalHooks.onBeforeInit) {
|
|
51
|
-
generalHooks.onBeforeInit();
|
|
52
|
+
generalHooks.onBeforeInit(app);
|
|
52
53
|
}
|
|
53
54
|
yield this.createRoutesByModelTree(modelTree, modelList);
|
|
54
55
|
if (generalHooks.onAfterInit) {
|
|
55
|
-
generalHooks.onAfterInit();
|
|
56
|
+
generalHooks.onAfterInit(app);
|
|
56
57
|
}
|
|
57
58
|
logger.info("Express routes have been created.");
|
|
58
59
|
});
|
|
@@ -146,7 +146,7 @@ const getRelatedData = (data, withArray, model, modelList, database, handler, re
|
|
|
146
146
|
for (const clientQuery of withArray) {
|
|
147
147
|
// Find the relation of the model. If the model doesn't have any relationship like the
|
|
148
148
|
// user wants, we can't show anything.
|
|
149
|
-
const definedRelation = model.relations.find((relation) => relation.
|
|
149
|
+
const definedRelation = model.relations.find((relation) => relation.name === clientQuery.relationship);
|
|
150
150
|
if (!definedRelation) {
|
|
151
151
|
throw new ApiError_1.default(`Undefined relation: ${clientQuery.relationship}`);
|
|
152
152
|
}
|
|
@@ -191,6 +191,15 @@ const getRelatedData = (data, withArray, model, modelList, database, handler, re
|
|
|
191
191
|
// };
|
|
192
192
|
// }),
|
|
193
193
|
// ];
|
|
194
|
+
console.log([
|
|
195
|
+
...workList.map((relationship) => {
|
|
196
|
+
return {
|
|
197
|
+
relationship,
|
|
198
|
+
fields: [],
|
|
199
|
+
children: [],
|
|
200
|
+
};
|
|
201
|
+
}),
|
|
202
|
+
]);
|
|
194
203
|
// We should check if the column is defined on the table.
|
|
195
204
|
const undefinedColumns = selectColumns.filter((column) => !foreignModel.columnNames.includes(column));
|
|
196
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";
|
|
@@ -52,8 +52,8 @@ export interface IFolders {
|
|
|
52
52
|
Models: string;
|
|
53
53
|
}
|
|
54
54
|
export interface IGeneralHooks {
|
|
55
|
-
onBeforeInit: () => void | null;
|
|
56
|
-
onAfterInit: () => void | null;
|
|
55
|
+
onBeforeInit: (app: Express) => void | null;
|
|
56
|
+
onAfterInit: (app: Express) => void | null;
|
|
57
57
|
}
|
|
58
58
|
export interface IHandlerBaseMiddleware {
|
|
59
59
|
handler: HandlerTypes[];
|
|
@@ -39,7 +39,9 @@ class ModelResolver {
|
|
|
39
39
|
if (typeof relationFunction !== "function") {
|
|
40
40
|
throw new Error(`Model relation definition should be a function: ${model.name}.${relationMethod}`);
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
const definition = relationFunction.call(model.instance);
|
|
43
|
+
definition.name = relationMethod;
|
|
44
|
+
model.relations.push(definition);
|
|
43
45
|
}
|
|
44
46
|
}
|
|
45
47
|
});
|
|
@@ -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,113 @@
|
|
|
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
|
+
const relationFunction = model.instance[expression.key];
|
|
40
|
+
if (typeof relationFunction === "function") {
|
|
41
|
+
const definition = relationFunction.call(model.instance);
|
|
42
|
+
const relationModel = this.models.find((item) => item.name === definition.model);
|
|
43
|
+
if (relationModel === undefined) {
|
|
44
|
+
throw new ApiError_1.default(`Undefined relation model: ${definition.model} (${expression.key})`);
|
|
45
|
+
}
|
|
46
|
+
const data = {
|
|
47
|
+
relationship: expression.key,
|
|
48
|
+
relationModel,
|
|
49
|
+
fields: [],
|
|
50
|
+
children: [],
|
|
51
|
+
};
|
|
52
|
+
if (expression.children.length > 0) {
|
|
53
|
+
this.resolveRelationsByKey(data.children, data.fields, expression.children, relationModel);
|
|
54
|
+
}
|
|
55
|
+
result.push(data);
|
|
56
|
+
}
|
|
57
|
+
else if (fields !== null) {
|
|
58
|
+
if (model.columnNames.includes(expression.key)) {
|
|
59
|
+
fields.push(expression.key);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
throw new ApiError_1.default(`It is not a field or a relation: ${expression.key}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
throw new ApiError_1.default(`Unknown expression: ${expression.key}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
toNestedArray(root, expression) {
|
|
71
|
+
const groups = this.splitByGroups(expression);
|
|
72
|
+
for (const group of groups) {
|
|
73
|
+
const key = this.getKey(group);
|
|
74
|
+
if (key) {
|
|
75
|
+
const child = {
|
|
76
|
+
key,
|
|
77
|
+
children: [],
|
|
78
|
+
};
|
|
79
|
+
this.toNestedArray(child, this.getGroupValue(group));
|
|
80
|
+
root.children.push(child);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
root.children.push(...group.split("|").map((field) => {
|
|
84
|
+
return {
|
|
85
|
+
key: field,
|
|
86
|
+
children: [],
|
|
87
|
+
};
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
splitByGroups(expression) {
|
|
93
|
+
const result = [];
|
|
94
|
+
let bracket = 0;
|
|
95
|
+
let startedAt = 0;
|
|
96
|
+
for (let index = 0; index < expression.length; index++) {
|
|
97
|
+
const char = expression[index];
|
|
98
|
+
if (char === "{") {
|
|
99
|
+
bracket++;
|
|
100
|
+
}
|
|
101
|
+
else if (char === "}") {
|
|
102
|
+
bracket--;
|
|
103
|
+
}
|
|
104
|
+
if (bracket === 0 && char === ",") {
|
|
105
|
+
result.push(expression.substring(startedAt, index));
|
|
106
|
+
startedAt = index + 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
result.push(expression.substring(startedAt, expression.length));
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
exports.default = WithQueryResolver;
|
|
@@ -4,4 +4,5 @@ import GeneralHookResolver from "./GeneralHookResolver";
|
|
|
4
4
|
import ModelMiddlewareResolver from "./ModelMiddlewareResolver";
|
|
5
5
|
import ModelResolver from "./ModelResolver";
|
|
6
6
|
import TransactionResolver from "./TransactionResolver";
|
|
7
|
-
|
|
7
|
+
import WithQueryResolver from "./WithQueryResolver";
|
|
8
|
+
export { FileResolver, FolderResolver, GeneralHookResolver, ModelMiddlewareResolver, ModelResolver, TransactionResolver, WithQueryResolver, };
|
|
@@ -3,7 +3,7 @@ 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.TransactionResolver = exports.ModelResolver = exports.ModelMiddlewareResolver = exports.GeneralHookResolver = 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"));
|
|
@@ -16,3 +16,5 @@ const ModelResolver_1 = __importDefault(require("./ModelResolver"));
|
|
|
16
16
|
exports.ModelResolver = ModelResolver_1.default;
|
|
17
17
|
const TransactionResolver_1 = __importDefault(require("./TransactionResolver"));
|
|
18
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;
|
|
@@ -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-rc19",
|
|
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",
|
|
@@ -41,9 +41,10 @@
|
|
|
41
41
|
"@babel/core": "^7.16.10",
|
|
42
42
|
"@babel/node": "^7.16.8",
|
|
43
43
|
"@babel/preset-env": "^7.16.11",
|
|
44
|
+
"@babel/preset-typescript": "^7.18.6",
|
|
44
45
|
"@babel/runtime": "^7.16.7",
|
|
45
46
|
"@types/pluralize": "^0.0.29",
|
|
46
|
-
"babel-jest": "^
|
|
47
|
+
"babel-jest": "^29.0.3",
|
|
47
48
|
"babel-loader": "^8.2.3",
|
|
48
49
|
"babel-plugin-module-resolver": "^4.1.0",
|
|
49
50
|
"babel-preset-minify": "^0.5.1",
|
|
@@ -55,11 +56,11 @@
|
|
|
55
56
|
"eslint-plugin-standard": "^5.0.0",
|
|
56
57
|
"eslint-plugin-unicorn": "^33.0.1",
|
|
57
58
|
"eslint-watch": "^7.0.0",
|
|
58
|
-
"jest": "^27.4.7",
|
|
59
59
|
"mysql": "^2.18.1",
|
|
60
60
|
"nodemon": "^2.0.15",
|
|
61
61
|
"pg": "^8.7.1",
|
|
62
62
|
"set-value": ">=4.1.0",
|
|
63
|
-
"sqlite3": "^5.0.2"
|
|
63
|
+
"sqlite3": "^5.0.2",
|
|
64
|
+
"ts-jest": "^29.0.1"
|
|
64
65
|
}
|
|
65
66
|
}
|