axe-api 0.19.1 → 0.20.0-rc10
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 +8 -0
- package/build/index.js +32 -0
- package/build/src/Builders/ModelTreeBuilder.d.ts +9 -0
- package/build/src/Builders/ModelTreeBuilder.js +64 -0
- package/build/src/Builders/RouterBuilder.d.ts +15 -0
- package/build/src/Builders/RouterBuilder.js +219 -0
- package/build/src/Builders/index.d.ts +3 -0
- package/build/src/Builders/index.js +10 -0
- package/build/src/Enums.d.ts +77 -0
- package/build/src/Enums.js +90 -0
- package/build/src/Exceptions/ApiError.d.ts +8 -0
- package/build/src/Exceptions/ApiError.js +12 -0
- package/build/src/Handlers/AllHandler.d.ts +4 -0
- package/build/src/Handlers/AllHandler.js +44 -0
- package/build/src/Handlers/DestroyHandler.d.ts +4 -0
- package/build/src/Handlers/DestroyHandler.js +37 -0
- package/build/src/Handlers/HandlerFactory.d.ts +6 -0
- package/build/src/Handlers/HandlerFactory.js +36 -0
- package/build/src/Handlers/Helpers.d.ts +14 -0
- package/build/src/Handlers/Helpers.js +231 -0
- package/build/src/Handlers/PaginateHandler.d.ts +4 -0
- package/build/src/Handlers/PaginateHandler.js +48 -0
- package/build/src/Handlers/PatchHandler.d.ts +4 -0
- package/build/src/Handlers/PatchHandler.js +62 -0
- package/build/src/Handlers/ShowHandler.d.ts +4 -0
- package/build/src/Handlers/ShowHandler.js +51 -0
- package/build/src/Handlers/StoreHandler.d.ts +4 -0
- package/build/src/Handlers/StoreHandler.js +59 -0
- package/build/src/Handlers/UpdateHandler.d.ts +4 -0
- package/build/src/Handlers/UpdateHandler.js +62 -0
- package/build/src/Interfaces.d.ts +165 -0
- package/build/src/Interfaces.js +3 -0
- package/build/src/Model.d.ts +24 -0
- package/build/src/Model.js +108 -0
- package/build/src/Resolvers/FileResolver.d.ts +5 -0
- package/build/src/Resolvers/FileResolver.js +76 -0
- package/build/src/Resolvers/FolderResolver.d.ts +5 -0
- package/build/src/Resolvers/FolderResolver.js +19 -0
- 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.d.ts +9 -0
- package/build/src/Resolvers/ModelResolver.js +101 -0
- package/build/src/Resolvers/TransactionResolver.d.ts +8 -0
- package/build/src/Resolvers/TransactionResolver.js +75 -0
- package/build/src/Resolvers/index.d.ts +7 -0
- package/build/src/Resolvers/index.js +18 -0
- package/build/src/Server.d.ts +8 -0
- package/build/src/Server.js +101 -0
- package/build/src/Services/DocumentationService.d.ts +9 -0
- package/build/src/Services/DocumentationService.js +22 -0
- package/build/src/Services/IoCService.d.ts +9 -0
- package/build/src/Services/IoCService.js +51 -0
- package/build/src/Services/LogService.d.ts +12 -0
- package/build/src/Services/LogService.js +41 -0
- package/build/src/Services/ModelListService.d.ts +8 -0
- package/build/src/Services/ModelListService.js +18 -0
- package/build/src/Services/ModelService.d.ts +20 -0
- package/build/src/Services/ModelService.js +38 -0
- package/build/src/Services/QueryService.d.ts +39 -0
- package/build/src/Services/QueryService.js +447 -0
- package/build/src/Services/SchemaValidatorService.d.ts +12 -0
- package/build/src/Services/SchemaValidatorService.js +114 -0
- package/build/src/Services/index.d.ts +8 -0
- package/build/src/Services/index.js +20 -0
- package/build/src/constants.d.ts +23 -0
- package/build/src/constants.js +62 -0
- package/package.json +65 -56
- package/readme.md +152 -145
- package/.eslintrc.cjs +0 -24
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -32
- package/.github/workflows/auto-tag.yml +0 -15
- package/.github/workflows/npm-publish.yml +0 -18
- package/.github/workflows/test-integration.yml +0 -29
- package/.github/workflows/test-unit.yml +0 -23
- package/CHANGELOG.md +0 -144
- package/babel.config.cjs +0 -12
- package/index.js +0 -21
- package/jest.config.js +0 -4
- package/src/Server.js +0 -118
- package/src/constants.js +0 -148
- package/src/core/Config.js +0 -38
- package/src/core/Docs.js +0 -43
- package/src/core/HttpResponse.js +0 -10
- package/src/core/IoC.js +0 -41
- package/src/core/Logger.js +0 -46
- package/src/core/Model.js +0 -86
- package/src/core/QueryParser.js +0 -544
- package/src/handlers/all.js +0 -73
- package/src/handlers/destroy.js +0 -50
- package/src/handlers/helpers.js +0 -320
- package/src/handlers/index.js +0 -9
- package/src/handlers/paginate.js +0 -77
- package/src/handlers/patch.js +0 -95
- package/src/handlers/show.js +0 -81
- package/src/handlers/store.js +0 -82
- package/src/handlers/update.js +0 -92
- package/src/resolvers/checkModelColumns.js +0 -113
- package/src/resolvers/detectDbColumns.js +0 -42
- package/src/resolvers/getModelInstanceArray.js +0 -27
- package/src/resolvers/getModelTree.js +0 -63
- package/src/resolvers/index.js +0 -17
- package/src/resolvers/setExpressRoutes.js +0 -286
- package/src/resolvers/setModelHooks.js +0 -25
- package/src/resolvers/setModelRelations.js +0 -41
package/src/core/Model.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import pluralize from "pluralize";
|
|
2
|
-
import { snakeCase } from "snake-case";
|
|
3
|
-
import { RELATIONSHIPS, DEFAULT_HANDLERS } from "./../constants.js";
|
|
4
|
-
|
|
5
|
-
class Model {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.relations = [];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
get primaryKey() {
|
|
11
|
-
return "id";
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
get table() {
|
|
15
|
-
return pluralize(snakeCase(this.constructor.name));
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
get fillable() {
|
|
19
|
-
return [];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
get validations() {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
get handlers() {
|
|
27
|
-
return [...DEFAULT_HANDLERS];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
get middlewares() {
|
|
31
|
-
return [];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
get hiddens() {
|
|
35
|
-
return [];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
get createdAtColumn() {
|
|
39
|
-
return "created_at";
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
get updatedAtColumn() {
|
|
43
|
-
return "updated_at";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
get transaction() {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
get ignore() {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
hasMany(relatedModel, primaryKey = "id", foreignKey = null) {
|
|
55
|
-
if (!foreignKey) {
|
|
56
|
-
const currentModelName = pluralize.singular(
|
|
57
|
-
this.constructor.name.toLowerCase()
|
|
58
|
-
);
|
|
59
|
-
foreignKey = `${currentModelName}_id`;
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
type: RELATIONSHIPS.HAS_MANY,
|
|
63
|
-
model: relatedModel,
|
|
64
|
-
primaryKey,
|
|
65
|
-
foreignKey,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
hasOne(relatedModel, primaryKey = "id", foreignKey = null) {
|
|
70
|
-
if (!foreignKey) {
|
|
71
|
-
foreignKey = `${pluralize.singular(relatedModel.toLowerCase())}_id`;
|
|
72
|
-
}
|
|
73
|
-
return {
|
|
74
|
-
type: RELATIONSHIPS.HAS_ONE,
|
|
75
|
-
model: relatedModel,
|
|
76
|
-
primaryKey,
|
|
77
|
-
foreignKey,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
belongsTo(relatedModel, primaryKey, foreignKey) {
|
|
82
|
-
return this.hasOne(relatedModel, foreignKey, primaryKey);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export default Model;
|
package/src/core/QueryParser.js
DELETED
|
@@ -1,544 +0,0 @@
|
|
|
1
|
-
import HttpResponse from "./../core/HttpResponse.js";
|
|
2
|
-
import { RELATIONSHIPS } from "../constants.js";
|
|
3
|
-
|
|
4
|
-
class QueryParser {
|
|
5
|
-
constructor({ model, models }) {
|
|
6
|
-
this.model = model;
|
|
7
|
-
this.models = models;
|
|
8
|
-
this.createdJoins = [];
|
|
9
|
-
this.relationColumns = [];
|
|
10
|
-
this.usedConditionColumns = new Set();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
applyFields(query, fields) {
|
|
14
|
-
// Users should be able to select some fields to show.
|
|
15
|
-
if (!Array.isArray(fields)) {
|
|
16
|
-
query.select(`${this.model.instance.table}.${fields}`);
|
|
17
|
-
} else if (fields.length > 0 && fields != "*") {
|
|
18
|
-
const fullPathFields = fields.map((field) => {
|
|
19
|
-
if (field.includes(".") === false) {
|
|
20
|
-
return `${this.model.instance.table}.${field}`;
|
|
21
|
-
}
|
|
22
|
-
return field;
|
|
23
|
-
});
|
|
24
|
-
query.select([...fullPathFields, ...this.relationColumns]);
|
|
25
|
-
} else {
|
|
26
|
-
query.select(`${this.model.instance.table}.*`);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
applySorting(query, sort) {
|
|
31
|
-
if (sort.length === 0) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
sort.forEach((item) => {
|
|
36
|
-
query.orderBy(item.field, item.type);
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
applyWheresInsideGroup(sub, ruleSet) {
|
|
41
|
-
// If there is not any query, we don't have to filter the data.
|
|
42
|
-
if (!ruleSet) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (Array.isArray(ruleSet)) {
|
|
47
|
-
for (const item of ruleSet) {
|
|
48
|
-
// If the item is not an array, it means that it is a standard condition
|
|
49
|
-
if (Array.isArray(item) === false) {
|
|
50
|
-
this._applyConditionRule(sub, item);
|
|
51
|
-
} else {
|
|
52
|
-
// If the item is an array, we should create the query recursively.
|
|
53
|
-
if (item[0].prefix === "or") {
|
|
54
|
-
sub.orWhere((sub) => {
|
|
55
|
-
this.applyWheresInsideGroup(sub, item);
|
|
56
|
-
});
|
|
57
|
-
} else {
|
|
58
|
-
sub.where((sub) => {
|
|
59
|
-
this.applyWheresInsideGroup(sub, item);
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
this._applyConditionRule(sub, ruleSet);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
applyWheres(query, ruleSet) {
|
|
70
|
-
query.where((sub) => {
|
|
71
|
-
this.applyWheresInsideGroup(sub, ruleSet);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
this._applyRelatedQueryJoins(query, ruleSet);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
get(query) {
|
|
78
|
-
const conditions = this._parseSections(this._getSections(query));
|
|
79
|
-
const usedColumns = this._getUsedColumns(conditions);
|
|
80
|
-
const undefinedColumns = usedColumns.filter((columnName) => {
|
|
81
|
-
let currentModel = this.model;
|
|
82
|
-
let realColumName = columnName;
|
|
83
|
-
if (columnName.includes(".")) {
|
|
84
|
-
const [table, splittedColumnName] = columnName.split(".");
|
|
85
|
-
currentModel = this.models.find(
|
|
86
|
-
(model) => model.instance.table === table
|
|
87
|
-
);
|
|
88
|
-
realColumName = splittedColumnName;
|
|
89
|
-
}
|
|
90
|
-
return !currentModel.instance.columnNames.includes(realColumName);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
if (undefinedColumns.length > 0) {
|
|
94
|
-
throw new HttpResponse(400, {
|
|
95
|
-
message: `Undefined column names: ${undefinedColumns.join(",")}`,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return conditions;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
_getUsedColumns(conditions) {
|
|
103
|
-
return [
|
|
104
|
-
...conditions.fields,
|
|
105
|
-
...conditions.sort.map((item) => item.field),
|
|
106
|
-
...Array.from(this.usedConditionColumns),
|
|
107
|
-
];
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
_applyConditionRule(sub, ruleSet) {
|
|
111
|
-
const method = this._getConditionMethodName(ruleSet);
|
|
112
|
-
const zeroArguments = ["Null", "NotNull"];
|
|
113
|
-
const oneArguments = ["In", "NotIn", "Between", "NotBetween"];
|
|
114
|
-
|
|
115
|
-
const fullFieldPath = `${ruleSet.table}.${ruleSet.field}`;
|
|
116
|
-
|
|
117
|
-
if (zeroArguments.indexOf(ruleSet.condition) > -1) {
|
|
118
|
-
return sub[`${method}${ruleSet.condition}`](fullFieldPath);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (oneArguments.indexOf(ruleSet.condition) > -1) {
|
|
122
|
-
return sub[`${method}${ruleSet.condition}`](fullFieldPath, ruleSet.value);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return sub[method](fullFieldPath, ruleSet.condition, ruleSet.value);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
_applyRelatedQueryJoins(query, ruleSet) {
|
|
129
|
-
if (!ruleSet) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (Array.isArray(ruleSet)) {
|
|
134
|
-
for (const item of ruleSet) {
|
|
135
|
-
// If the item is not an array, it means that it is a standard condition
|
|
136
|
-
if (Array.isArray(item) === false) {
|
|
137
|
-
this._applyJoinIfNecessary(query, item);
|
|
138
|
-
} else {
|
|
139
|
-
this._applyRelatedQueryJoins(query, item);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
} else {
|
|
143
|
-
this._applyJoinIfNecessary(query, ruleSet);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
_applyJoinIfNecessary(query, ruleSet) {
|
|
148
|
-
if (ruleSet.table !== this.model.instance.table) {
|
|
149
|
-
this._addJoinOnce(query, ruleSet);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
_addJoinOnce(query, { model, relation }) {
|
|
154
|
-
if (this.createdJoins.includes(relation.name)) {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const tableName = model.instance.table;
|
|
159
|
-
const primaryKey = `${model.instance.table}.${relation.primaryKey}`;
|
|
160
|
-
const foreignKey = `${this.model.instance.table}.${relation.foreignKey}`;
|
|
161
|
-
query.leftJoin(tableName, primaryKey, foreignKey);
|
|
162
|
-
this.createdJoins.push(relation.name);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
_getSections(query) {
|
|
166
|
-
if (typeof query !== "object") {
|
|
167
|
-
throw new Error("You have to send an object to get sections.");
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const sections = {
|
|
171
|
-
q: null,
|
|
172
|
-
page: null,
|
|
173
|
-
per_page: null,
|
|
174
|
-
sort: null,
|
|
175
|
-
fields: null,
|
|
176
|
-
with: null,
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
for (const key of Object.keys(query)) {
|
|
180
|
-
if (sections[key] === undefined) {
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
sections[key] = query[key];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return sections;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
_parseSections(sections) {
|
|
191
|
-
if (sections.q) {
|
|
192
|
-
const queryContent = sections.q.replace(/%20/g, "").replace(/ /g, "");
|
|
193
|
-
|
|
194
|
-
// Users can send an unacceptable query string. We shouldn't allow them to
|
|
195
|
-
// send unacceptable structure because of security reasons.
|
|
196
|
-
try {
|
|
197
|
-
sections.q = JSON.parse(queryContent);
|
|
198
|
-
} catch (err) {
|
|
199
|
-
throw new HttpResponse(400, {
|
|
200
|
-
message: `Unacceptable query string: ${queryContent}`,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
sections.page = this._parsePage(sections.page);
|
|
206
|
-
sections.per_page = this._parsePerPage(sections.per_page);
|
|
207
|
-
sections.fields = this._parseFields(sections.fields);
|
|
208
|
-
sections.sort = this._parseSortingOptions(sections.sort);
|
|
209
|
-
sections.q = this._parseCondition(sections.q);
|
|
210
|
-
sections.with = this._parseWith(this._parseWithSections(sections.with));
|
|
211
|
-
this._addRelationColumns(sections.with);
|
|
212
|
-
|
|
213
|
-
return sections;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
_parsePage(content) {
|
|
217
|
-
const value = parseInt(content);
|
|
218
|
-
|
|
219
|
-
if (isNaN(value)) {
|
|
220
|
-
return 1;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (value <= 0) {
|
|
224
|
-
return 1;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return value;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
_parsePerPage(content) {
|
|
231
|
-
const value = parseInt(content);
|
|
232
|
-
|
|
233
|
-
if (isNaN(value) || value <= 1 || value > 10000) {
|
|
234
|
-
return 10;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return value;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
_parseFields(content) {
|
|
241
|
-
if (!content) {
|
|
242
|
-
return [];
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// User should be able to select "all" fields.
|
|
246
|
-
if (content.trim() === "*") {
|
|
247
|
-
return "*";
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const fields = content.split(",");
|
|
251
|
-
fields.forEach((field) => {
|
|
252
|
-
this._shouldBeAcceptableColumn(field);
|
|
253
|
-
});
|
|
254
|
-
return fields;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
_parseSortingOptions(content) {
|
|
258
|
-
// If there is not any sorting options, we don't have to return any value
|
|
259
|
-
if (!content) {
|
|
260
|
-
return [];
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const result = [];
|
|
264
|
-
for (let field of content.split(",")) {
|
|
265
|
-
let type = "ASC";
|
|
266
|
-
if (field.indexOf("-") === 0) {
|
|
267
|
-
type = "DESC";
|
|
268
|
-
field = field.substr(1);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (field.indexOf("+") === 0) {
|
|
272
|
-
field = field.substr(1);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
this._shouldBeAcceptableColumn(field);
|
|
276
|
-
result.push({
|
|
277
|
-
field,
|
|
278
|
-
type,
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
return result;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
_parseConditions(conditions) {
|
|
285
|
-
if (!Array.isArray(conditions)) {
|
|
286
|
-
throw new Error("An array should be sent to parseConditions() method.");
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return conditions.map((condition) => {
|
|
290
|
-
return this._parseCondition(condition);
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
_parseCondition(content) {
|
|
295
|
-
if (Array.isArray(content)) {
|
|
296
|
-
return this._parseConditions(content);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (!content) {
|
|
300
|
-
return null;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const wheres = [];
|
|
304
|
-
for (const key in content) {
|
|
305
|
-
wheres.push(this._parseConditionObject(content, key));
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return wheres;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
_parseConditionObject(content, key) {
|
|
312
|
-
const where = {
|
|
313
|
-
prefix: null,
|
|
314
|
-
model: this.model,
|
|
315
|
-
table: this.model.instance.table,
|
|
316
|
-
field: key,
|
|
317
|
-
condition: "=",
|
|
318
|
-
value: content[key],
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
// Sometimes we can have basic OR operations for queries
|
|
322
|
-
if (where.field.indexOf("$or.") === 0) {
|
|
323
|
-
where.prefix = "or";
|
|
324
|
-
where.field = where.field.replace("$or.", "");
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (where.field.indexOf("$and.") === 0) {
|
|
328
|
-
where.prefix = "and";
|
|
329
|
-
where.field = where.field.replace("$and.", "");
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// If there is not any value, it means that we should check nullable values
|
|
333
|
-
if (where.value === null) {
|
|
334
|
-
// If the client wants to see not nullable values
|
|
335
|
-
if (this._hasSpecialStructure(where.field, ".$not")) {
|
|
336
|
-
where.field = where.field.replace(".$not", "");
|
|
337
|
-
where.condition = "NotNull";
|
|
338
|
-
} else {
|
|
339
|
-
// So, it means that the clients wants to see null valus
|
|
340
|
-
where.condition = "Null";
|
|
341
|
-
}
|
|
342
|
-
} else {
|
|
343
|
-
// If there is value, we should check it
|
|
344
|
-
this._applySpecialCondition(where, "$not", "<>");
|
|
345
|
-
this._applySpecialCondition(where, "$gt", ">");
|
|
346
|
-
this._applySpecialCondition(where, "$gte", ">=");
|
|
347
|
-
this._applySpecialCondition(where, "$lt", "<");
|
|
348
|
-
this._applySpecialCondition(where, "$lte", "<=");
|
|
349
|
-
this._applySpecialCondition(where, "$like", "LIKE");
|
|
350
|
-
this._applySpecialCondition(where, "$notLike", "NOT LIKE");
|
|
351
|
-
this._applySpecialCondition(where, "$in", "In");
|
|
352
|
-
this._applySpecialCondition(where, "$notIn", "NotIn");
|
|
353
|
-
this._applySpecialCondition(where, "$between", "Between");
|
|
354
|
-
this._applySpecialCondition(where, "$notBetween", "NotBetween");
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (where.condition === "In" || where.condition === "NotIn") {
|
|
358
|
-
where.value = where.value.split(",");
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if (where.condition === "Between" || where.condition === "NotBetween") {
|
|
362
|
-
where.value = where.value.split(":");
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (where.condition === "LIKE" || where.condition === "NOT LIKE") {
|
|
366
|
-
where.value = where.value.replace(/\*/g, "%");
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// This means that the condition is related with another table
|
|
370
|
-
if (where.field.includes(".")) {
|
|
371
|
-
const [relationName, field] = where.field.split(".");
|
|
372
|
-
|
|
373
|
-
const relation = this.model.instance.relations.find(
|
|
374
|
-
(item) =>
|
|
375
|
-
item.name === relationName && item.type === RELATIONSHIPS.HAS_ONE
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
if (!relation) {
|
|
379
|
-
throw new HttpResponse(400, {
|
|
380
|
-
message: `Unacceptable query field: ${relationName}.${field}`,
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const relatedModel = this.models.find(
|
|
385
|
-
(item) => item.name === relation.model
|
|
386
|
-
);
|
|
387
|
-
|
|
388
|
-
if (!relatedModel) {
|
|
389
|
-
throw new HttpResponse(400, {
|
|
390
|
-
message: `Undefined model name: ${relation.model}`,
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
where.model = relatedModel;
|
|
395
|
-
where.table = relatedModel.instance.table;
|
|
396
|
-
where.relation = relation;
|
|
397
|
-
where.field = field;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
this._shouldBeAcceptableColumn(where.field);
|
|
401
|
-
this.usedConditionColumns.add(`${where.table}.${where.field}`);
|
|
402
|
-
|
|
403
|
-
return where;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
_parseWithSections(content) {
|
|
407
|
-
if (!content) {
|
|
408
|
-
return [];
|
|
409
|
-
}
|
|
410
|
-
return content.split(",");
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
_parseWith(items) {
|
|
414
|
-
const result = [];
|
|
415
|
-
for (const item of items) {
|
|
416
|
-
let relationship = item;
|
|
417
|
-
let fields = [];
|
|
418
|
-
let children = [];
|
|
419
|
-
|
|
420
|
-
const columnIndex = relationship.indexOf("{");
|
|
421
|
-
if (columnIndex > -1) {
|
|
422
|
-
fields = this._splitWithRecursive(
|
|
423
|
-
relationship.substr(
|
|
424
|
-
columnIndex + 1,
|
|
425
|
-
relationship.length - columnIndex - 2
|
|
426
|
-
)
|
|
427
|
-
);
|
|
428
|
-
|
|
429
|
-
relationship = relationship.substr(0, columnIndex);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// We are checking there is any children
|
|
433
|
-
children = fields.filter(
|
|
434
|
-
(field) => field.indexOf("{") > -1 || field.indexOf(".") > -1
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
// Field list shouldn't have any related table
|
|
438
|
-
fields = fields.filter(
|
|
439
|
-
(field) => field.indexOf("{") === -1 && field.indexOf(".") === -1
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
// We should validate fields are correct.
|
|
443
|
-
fields.forEach((field) => {
|
|
444
|
-
this._shouldBeAcceptableColumn(field);
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
// We should calculate recursivly all of childre
|
|
448
|
-
children = this._parseWith(children);
|
|
449
|
-
|
|
450
|
-
result.push({
|
|
451
|
-
relationship,
|
|
452
|
-
fields,
|
|
453
|
-
children,
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
return result;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
_splitWithRecursive(content) {
|
|
460
|
-
const result = [];
|
|
461
|
-
let startAt = 0;
|
|
462
|
-
let subcounter = 0;
|
|
463
|
-
for (let position = 0; position < content.length; position++) {
|
|
464
|
-
const current = content[position];
|
|
465
|
-
|
|
466
|
-
if (current === "{") {
|
|
467
|
-
subcounter++;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
if (current === "}") {
|
|
471
|
-
subcounter--;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
if (current === "|" && subcounter === 0) {
|
|
475
|
-
result.push(content.substr(startAt, position - startAt));
|
|
476
|
-
startAt = position + 1;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
result.push(content.substr(startAt));
|
|
481
|
-
return result;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
_applySpecialCondition(where, structure, condition) {
|
|
485
|
-
structure = `.${structure}`;
|
|
486
|
-
if (this._hasSpecialStructure(where.field, structure)) {
|
|
487
|
-
where.field = where.field.replace(structure, "");
|
|
488
|
-
where.condition = condition;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
_addRelationColumns(withs) {
|
|
493
|
-
withs.forEach((item) => {
|
|
494
|
-
const relation = this.model.instance.relations.find(
|
|
495
|
-
(i) => i.name === item.relationship
|
|
496
|
-
);
|
|
497
|
-
if (!relation) {
|
|
498
|
-
throw new HttpResponse(400, {
|
|
499
|
-
message: `Undefined relation: ${item.relationship}`,
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
this.relationColumns.push(
|
|
504
|
-
`${this.model.instance.table}.${relation.foreignKey}`
|
|
505
|
-
);
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
_getConditionMethodName(ruleSet) {
|
|
510
|
-
if (ruleSet.prefix === "or") {
|
|
511
|
-
return "orWhere";
|
|
512
|
-
}
|
|
513
|
-
return "where";
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
_hasSpecialStructure(field, structure) {
|
|
517
|
-
if (field.indexOf(structure) === -1) {
|
|
518
|
-
return false;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
if (field.indexOf(structure) === field.length - structure.length) {
|
|
522
|
-
return true;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
return false;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
_shouldBeAcceptableColumn(field) {
|
|
529
|
-
const regex = /^[0-9,a-z,A-Z_.]+$/;
|
|
530
|
-
if (!field.match(regex)) {
|
|
531
|
-
throw new HttpResponse(400, {
|
|
532
|
-
message: `Unacceptable field name: ${field}`,
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
if (field.indexOf(".") === 0 || field.indexOf(".") === field.length - 1) {
|
|
537
|
-
throw new HttpResponse(400, {
|
|
538
|
-
message: `You have to define the column specefically: ${field}`,
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
export default QueryParser;
|
package/src/handlers/all.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
callHooks,
|
|
3
|
-
getRelatedData,
|
|
4
|
-
filterHiddenFields,
|
|
5
|
-
serializeData,
|
|
6
|
-
addForeignKeyQuery,
|
|
7
|
-
} from "./helpers.js";
|
|
8
|
-
import { HOOK_FUNCTIONS, HANDLERS } from "./../constants.js";
|
|
9
|
-
import QueryParser from "./../core/QueryParser.js";
|
|
10
|
-
|
|
11
|
-
export default async (context) => {
|
|
12
|
-
const { request, response, model, models, trx, relation, parentModel } =
|
|
13
|
-
context;
|
|
14
|
-
|
|
15
|
-
const queryParser = new QueryParser({ model, models });
|
|
16
|
-
|
|
17
|
-
// We should parse URL query string to use as condition in Lucid query
|
|
18
|
-
const conditions = queryParser.get(request.query);
|
|
19
|
-
|
|
20
|
-
// Creating a new database query
|
|
21
|
-
const query = trx.from(model.instance.table);
|
|
22
|
-
|
|
23
|
-
// Users should be able to select some fields to show.
|
|
24
|
-
queryParser.applyFields(query, conditions.fields);
|
|
25
|
-
|
|
26
|
-
// Binding parent id if there is.
|
|
27
|
-
addForeignKeyQuery(request, query, relation, parentModel);
|
|
28
|
-
|
|
29
|
-
// Users should be able to filter records
|
|
30
|
-
queryParser.applyWheres(query, conditions.q);
|
|
31
|
-
|
|
32
|
-
await callHooks(model, HOOK_FUNCTIONS.onBeforeAll, {
|
|
33
|
-
...context,
|
|
34
|
-
conditions,
|
|
35
|
-
query,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// User should be able to select sorting fields and types
|
|
39
|
-
queryParser.applySorting(query, conditions.sort);
|
|
40
|
-
|
|
41
|
-
const result = await query;
|
|
42
|
-
|
|
43
|
-
// We should try to get related data if there is any
|
|
44
|
-
await getRelatedData(
|
|
45
|
-
result.data,
|
|
46
|
-
conditions.with,
|
|
47
|
-
model,
|
|
48
|
-
models,
|
|
49
|
-
trx,
|
|
50
|
-
HANDLERS.ALL,
|
|
51
|
-
request
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
await callHooks(model, HOOK_FUNCTIONS.onAfterAll, {
|
|
55
|
-
...context,
|
|
56
|
-
result,
|
|
57
|
-
conditions,
|
|
58
|
-
query,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// Serializing the data by the model's serialize method
|
|
62
|
-
result.data = await serializeData(
|
|
63
|
-
result.data,
|
|
64
|
-
model.instance.serialize,
|
|
65
|
-
HANDLERS.ALL,
|
|
66
|
-
request
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
// Filtering hidden fields from the response data.
|
|
70
|
-
filterHiddenFields(result.data, model.instance.hiddens);
|
|
71
|
-
|
|
72
|
-
return response.json(result);
|
|
73
|
-
};
|