axe-api 0.19.0 → 0.20.0-rc1

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.
@@ -1,92 +0,0 @@
1
- import {
2
- getFormData,
3
- getFormValidation,
4
- callHooks,
5
- filterHiddenFields,
6
- bindTimestampValues,
7
- serializeData,
8
- addForeignKeyQuery,
9
- } from "./helpers.js";
10
- import Validator from "validatorjs";
11
- import { HOOK_FUNCTIONS, TIMESTAMP_COLUMNS, HANDLERS } from "./../constants.js";
12
- import HttpResponse from "./../core/HttpResponse.js";
13
-
14
- export default async (context) => {
15
- const { request, response, model, trx, relation, parentModel } = context;
16
-
17
- const query = trx.from(model.instance.table);
18
-
19
- // If there is a relation, we should bind it
20
- addForeignKeyQuery(request, query, relation, parentModel);
21
-
22
- await callHooks(model, HOOK_FUNCTIONS.onBeforeUpdateQuery, {
23
- ...context,
24
- query,
25
- });
26
-
27
- let item = await query
28
- .where(model.instance.primaryKey, request.params[model.instance.primaryKey])
29
- .first();
30
- if (!item) {
31
- throw new HttpResponse(404, {
32
- message: `The item is not found on ${model.name}.`,
33
- });
34
- }
35
-
36
- await callHooks(model, HOOK_FUNCTIONS.onAfterUpdateQuery, {
37
- ...context,
38
- item,
39
- query,
40
- });
41
-
42
- const formData = getFormData(request, model.instance.fillable);
43
-
44
- const formValidationRules = getFormValidation(
45
- request.method,
46
- model.instance.validations
47
- );
48
-
49
- if (formValidationRules) {
50
- const validation = new Validator(formData, formValidationRules);
51
- if (validation.fails()) {
52
- return response.status(400).json(validation.errors);
53
- }
54
- }
55
-
56
- // We should bind the timestamp values
57
- bindTimestampValues(formData, [TIMESTAMP_COLUMNS.UPDATED_AT], model);
58
-
59
- await callHooks(model, HOOK_FUNCTIONS.onBeforeUpdate, {
60
- ...context,
61
- item,
62
- formData,
63
- query,
64
- });
65
-
66
- await query
67
- .where(model.instance.primaryKey, item[model.instance.primaryKey])
68
- .update(formData);
69
- item = await trx(model.instance.table)
70
- .where(model.instance.primaryKey, item[model.instance.primaryKey])
71
- .first();
72
-
73
- await callHooks(model, HOOK_FUNCTIONS.onAfterUpdate, {
74
- ...context,
75
- item,
76
- formData,
77
- query,
78
- });
79
-
80
- // Serializing the data by the model's serialize method
81
- item = await serializeData(
82
- item,
83
- model.instance.serialize,
84
- HANDLERS.UPDATE,
85
- request
86
- );
87
-
88
- // Filtering hidden fields from the response data.
89
- filterHiddenFields([item], model.instance.hiddens);
90
-
91
- return response.json(item);
92
- };
@@ -1,113 +0,0 @@
1
- import { RELATIONSHIPS } from "./../constants.js";
2
-
3
- const getModelFillableColumns = (model) => {
4
- if (!model.instance.fillable) {
5
- return [];
6
- }
7
-
8
- if (!model.instance.fillable.POST && !model.instance.fillable.PUT) {
9
- return model.instance.fillable;
10
- }
11
-
12
- return [...model.instance.fillable.POST, ...model.instance.fillable.PUT];
13
- };
14
-
15
- const getModelHiddenColumns = (model) => {
16
- if (!model.instance.hiddens) {
17
- return [];
18
- }
19
-
20
- return model.instance.hiddens;
21
- };
22
-
23
- const getModelFormValidationColumns = (model) => {
24
- if (!model.instance.validations) {
25
- return [];
26
- }
27
-
28
- if (!model.instance.validations.POST && !model.instance.validations.PUT) {
29
- return Object.keys(model.instance.validations);
30
- }
31
-
32
- return [
33
- ...Object.keys(
34
- model.instance.validations.POST ? model.instance.validations.POST : {}
35
- ),
36
- ...Object.keys(
37
- model.instance.validations.PUT ? model.instance.validations.PUT : {}
38
- ),
39
- ];
40
- };
41
-
42
- const getTimestampsColumns = (model) => {
43
- const columns = [];
44
-
45
- if (model.instance.createdAtColumn) {
46
- columns.push(model.instance.createdAtColumn);
47
- }
48
-
49
- if (model.instance.updatedAtColumn) {
50
- columns.push(model.instance.updatedAtColumn);
51
- }
52
-
53
- return columns;
54
- };
55
-
56
- const checkModelColumnsOrFail = (model, modelColumns) => {
57
- const undefinedColumns = modelColumns.filter(
58
- (modelColumn) => !model.instance.columnNames.includes(modelColumn)
59
- );
60
- if (undefinedColumns.length > 0) {
61
- throw new Error(
62
- `${
63
- model.name
64
- } model doesn't have the following columns on the database; "${
65
- model.instance.table
66
- }.${undefinedColumns.join(",")}"`
67
- );
68
- }
69
- };
70
-
71
- const checkHasManyRelation = (models, model, relation) => {
72
- checkModelColumnsOrFail(model, [relation.primaryKey]);
73
- const relatedModel = models.find((item) => item.name === relation.model);
74
- if (!relatedModel) {
75
- throw new Error(`Undefined related model: ${relation.model}`);
76
- }
77
- checkModelColumnsOrFail(relatedModel, [relation.foreignKey]);
78
- };
79
-
80
- const checkHasOneRelation = (models, model, relation) => {
81
- checkModelColumnsOrFail(model, [relation.foreignKey]);
82
- const relatedModel = models.find((item) => item.name === relation.model);
83
- if (!relatedModel) {
84
- throw new Error(`Undefined related model: ${relation.model}`);
85
- }
86
- checkModelColumnsOrFail(relatedModel, [relation.primaryKey]);
87
- };
88
-
89
- const checkerByRelationTypes = {
90
- [RELATIONSHIPS.HAS_MANY]: checkHasManyRelation,
91
- [RELATIONSHIPS.HAS_ONE]: checkHasOneRelation,
92
- };
93
-
94
- const checkRelationColumnsOrFail = (models, model) => {
95
- for (const relation of model.instance.relations) {
96
- const checker = checkerByRelationTypes[relation.type];
97
- if (!checker) {
98
- throw new Error(`Undefined relation type: ${relation.type}`);
99
- }
100
- checker(models, model, relation);
101
- }
102
- };
103
-
104
- export default (models) => {
105
- models.forEach((model) => {
106
- checkModelColumnsOrFail(model, getModelFillableColumns(model));
107
- checkModelColumnsOrFail(model, getModelFormValidationColumns(model));
108
- checkModelColumnsOrFail(model, getModelHiddenColumns(model));
109
- checkModelColumnsOrFail(model, getTimestampsColumns(model));
110
- checkModelColumnsOrFail(model, [model.instance.primaryKey]);
111
- checkRelationColumnsOrFail(models, model);
112
- });
113
- };
@@ -1,42 +0,0 @@
1
- import schemaInspector from "knex-schema-inspector";
2
- import IoC from "./../core/IoC.js";
3
-
4
- const getDatabaseColumns = async () => {
5
- const database = await IoC.use("Database");
6
- const inspector = schemaInspector.default(database);
7
- const databaseColumns = [];
8
- for (const table of await inspector.tables()) {
9
- const columns = await inspector.columnInfo(table);
10
- databaseColumns.push(
11
- ...columns.map((column) => {
12
- return {
13
- ...column,
14
- table_name: table,
15
- };
16
- })
17
- );
18
- }
19
-
20
- return databaseColumns;
21
- };
22
-
23
- const bindModelColumns = (models, columns) => {
24
- for (const model of models) {
25
- model.instance.columns = columns.filter(
26
- (column) => column.table_name === model.instance.table
27
- );
28
-
29
- if (model.instance.columns.length === 0) {
30
- throw new Error(
31
- `The "${model.instance.table}" table doesn't have any column. Are you sure about the table name?`
32
- );
33
- }
34
-
35
- model.instance.columnNames = model.instance.columns.map((i) => i.name);
36
- }
37
- };
38
-
39
- export default async (models) => {
40
- const columns = await getDatabaseColumns();
41
- bindModelColumns(models, columns);
42
- };
@@ -1,27 +0,0 @@
1
- import IoC from "./../core/IoC.js";
2
-
3
- export default async (appDirectory) => {
4
- const logger = await IoC.use("Logger");
5
- const fs = await IoC.use("fs");
6
- const path = await IoC.use("path");
7
- const url = await IoC.use("url");
8
-
9
- appDirectory = path.join(appDirectory, "Models");
10
- const models = [];
11
- const files = fs
12
- .readdirSync(appDirectory)
13
- .filter((file) => file.split(".").pop() === "js");
14
-
15
- for (const file of files) {
16
- const modelName = file.replace(".js", "");
17
- const modelFile = url.pathToFileURL(path.join(appDirectory, file)).href;
18
- const { default: Model } = await import(modelFile);
19
- models.push({
20
- name: modelName,
21
- instance: new Model(),
22
- });
23
- }
24
-
25
- logger.info("Models have been resolved!");
26
- return models;
27
- };
@@ -1,63 +0,0 @@
1
- import IoC from "./../core/IoC.js";
2
- import { RELATIONSHIPS } from "./../constants.js";
3
-
4
- const getChildModelNames = (model) => {
5
- return model.instance.relations
6
- .filter((item) => item.type === RELATIONSHIPS.HAS_MANY)
7
- .map((item) => item.model);
8
- };
9
-
10
- const _setChildrens = (model, models) => {
11
- const childModelNames = getChildModelNames(model);
12
- model.children = models.filter((item) => childModelNames.includes(item.name));
13
- for (const child of model.children) {
14
- _setChildrens(child, models);
15
- }
16
- };
17
-
18
- const getRootLevelOfTree = (models) => {
19
- const childModels = [];
20
- models.forEach((model) => {
21
- childModels.push(
22
- ...model.instance.relations
23
- .filter((relation) => relation.type === RELATIONSHIPS.HAS_MANY)
24
- .map((relation) => relation.model)
25
- );
26
- });
27
-
28
- return models.filter((model) => !childModels.includes(model.name));
29
- };
30
-
31
- const createRecursiveTree = (tree, models) => {
32
- for (const model of tree) {
33
- _setChildrens(model, models);
34
- }
35
- };
36
-
37
- const addNestedRoutes = (tree, models) => {
38
- // We should add recursive models
39
- models.forEach((model) => {
40
- const recursiveRelations = model.instance.relations.filter(
41
- (relation) => relation.model === model.name
42
- );
43
-
44
- if (recursiveRelations.length === 2) {
45
- tree.push({
46
- isRecursive: true,
47
- children: [],
48
- ...model,
49
- });
50
- }
51
- });
52
- };
53
-
54
- export default async (models) => {
55
- const logger = await IoC.use("Logger");
56
-
57
- const tree = getRootLevelOfTree(models);
58
- createRecursiveTree(tree, models);
59
- addNestedRoutes(tree, models);
60
-
61
- logger.info("Model tree map has been created.");
62
- return tree;
63
- };
@@ -1,17 +0,0 @@
1
- import getModelInstanceArray from "./getModelInstanceArray.js";
2
- import getModelTree from "./getModelTree.js";
3
- import setModelRelations from "./setModelRelations.js";
4
- import setModelHooks from "./setModelHooks.js";
5
- import setExpressRoutes from "./setExpressRoutes.js";
6
- import detectDbColumns from "./detectDbColumns.js";
7
- import checkModelColumns from "./checkModelColumns.js";
8
-
9
- export {
10
- getModelInstanceArray,
11
- getModelTree,
12
- setModelRelations,
13
- setModelHooks,
14
- setExpressRoutes,
15
- detectDbColumns,
16
- checkModelColumns,
17
- };
@@ -1,279 +0,0 @@
1
- import IoC from "./../core/IoC.js";
2
- import pluralize from "pluralize";
3
- import { paramCase, camelCase } from "change-case";
4
- import { RELATIONSHIPS, API_ROUTE_TEMPLATES } from "./../constants.js";
5
- import Handlers from "./../handlers/index.js";
6
-
7
- let Config = null;
8
-
9
- const setTransactionOption = (option, handler, defaultValue) => {
10
- if (Array.isArray(option)) {
11
- if (option.some((i) => i.handler === handler)) {
12
- defaultValue = option.find((i) => i.handler === handler).transaction;
13
- }
14
- } else {
15
- defaultValue = option;
16
- }
17
-
18
- return defaultValue;
19
- };
20
-
21
- const hasTransaction = (config, model, handler) => {
22
- const global = config.Application.transaction;
23
- const local = model.instance.transaction;
24
- let privilegedOption = false;
25
-
26
- if (global) {
27
- privilegedOption = setTransactionOption(global, handler, privilegedOption);
28
- }
29
-
30
- if (local !== null) {
31
- privilegedOption = setTransactionOption(local, handler, privilegedOption);
32
- }
33
-
34
- return privilegedOption;
35
- };
36
-
37
- const requestHandler = async (handler, req, res, next, context) => {
38
- try {
39
- context.trx = context.database;
40
- if (hasTransaction(Config, context.model, handler)) {
41
- context.trx = await context.database.transaction();
42
- }
43
- await Handlers[handler]({
44
- ...context,
45
- request: req,
46
- response: res,
47
- });
48
-
49
- if (context.trx.commit) {
50
- await context.trx.commit();
51
- }
52
- } catch (error) {
53
- if (context.trx.rollback) {
54
- await context.trx.rollback();
55
- }
56
-
57
- if (error.type === "HttpResponse") {
58
- return res.status(error.status).json(error.content);
59
- }
60
-
61
- next(error);
62
- }
63
- };
64
-
65
- const ucFirst = (string) => {
66
- return string.charAt(0).toUpperCase() + string.slice(1);
67
- };
68
-
69
- const getResourcePath = (model, relation) => {
70
- return relation
71
- ? relation.resource
72
- : paramCase(pluralize.plural(model.name)).toLowerCase();
73
- };
74
-
75
- const getPrimaryKeyName = (model) => {
76
- return (
77
- pluralize.singular(model.name).toLowerCase() +
78
- ucFirst(model.instance.primaryKey)
79
- );
80
- };
81
-
82
- const createChildRoutes = async (model, models, resource, urlPrefix) => {
83
- if (model.children.length === 0) {
84
- return;
85
- }
86
-
87
- // We should different parameter name for child routes
88
- const primaryKey = getPrimaryKeyName(model);
89
- const subRelations = model.instance.relations.filter(
90
- (item) => item.type === RELATIONSHIPS.HAS_MANY
91
- );
92
- for (const relation of subRelations) {
93
- const child = model.children.find((item) => item.name === relation.model);
94
- // It should be recursive
95
- await createRouteByModel(
96
- child,
97
- models,
98
- `${urlPrefix}${resource}/:${primaryKey}/`,
99
- model,
100
- relation
101
- );
102
- }
103
- };
104
-
105
- const createNestedRoutes = async (
106
- model,
107
- models,
108
- allowRecursive,
109
- urlPrefix,
110
- resource
111
- ) => {
112
- if (!model.isRecursive || !allowRecursive) {
113
- return;
114
- }
115
-
116
- // We should different parameter name for child routes
117
- const relation = model.instance.relations.find(
118
- (relation) =>
119
- relation.model === model.name && relation.type === RELATIONSHIPS.HAS_MANY
120
- );
121
-
122
- await createRouteByModel(
123
- model,
124
- models,
125
- `${urlPrefix}${resource}/:${camelCase(relation.foreignKey)}/`,
126
- model,
127
- relation,
128
- false
129
- );
130
- };
131
-
132
- const hasAMiddleware = (definition, handler) => {
133
- if (typeof definition === "function") {
134
- return true;
135
- }
136
-
137
- if (definition.handler === handler) {
138
- return true;
139
- }
140
-
141
- if (
142
- Array.isArray(definition.handler) &&
143
- definition.handler.includes(handler)
144
- ) {
145
- return true;
146
- }
147
-
148
- return false;
149
- };
150
-
151
- const getModelMiddlewares = (model, handler) => {
152
- const middlewares = [];
153
- if (model.instance.middlewares.length > 0) {
154
- const filtered = model.instance.middlewares
155
- .filter((definition) => hasAMiddleware(definition, handler))
156
- .map((definition) => {
157
- if (typeof definition === "function") {
158
- return definition;
159
- }
160
- return definition.middleware;
161
- });
162
- middlewares.push(...filtered);
163
- }
164
- return middlewares;
165
- };
166
-
167
- const getRootPrefix = () => {
168
- if (Config.Application.prefix) {
169
- // NOSONAR
170
- return Config.Application.prefix.replace(/^\/|\/$/g, "");
171
- }
172
- return "api";
173
- };
174
-
175
- const createRouteByModel = async (
176
- model,
177
- models,
178
- urlPrefix = "",
179
- parentModel = null,
180
- relation = null,
181
- allowRecursive = true
182
- ) => {
183
- if (model.instance.ignore) {
184
- return;
185
- }
186
-
187
- const logger = await IoC.use("Logger");
188
- const app = await IoC.use("App");
189
- const database = await IoC.use("Database");
190
- const docs = await IoC.use("Docs");
191
-
192
- const context = {
193
- model,
194
- models,
195
- parentModel,
196
- relation,
197
- Config,
198
- database,
199
- logger,
200
- };
201
-
202
- const resource = getResourcePath(model, relation);
203
-
204
- // We create and handle routes by not duplicate so many lines.
205
- for (const handler of Object.keys(API_ROUTE_TEMPLATES)) {
206
- if (!model.instance.handlers.includes(handler)) {
207
- continue;
208
- }
209
-
210
- const routeTemplate = API_ROUTE_TEMPLATES[handler];
211
- const url = routeTemplate.url(
212
- getRootPrefix(),
213
- urlPrefix,
214
- resource,
215
- model.instance.primaryKey
216
- );
217
- logger.debug(`Model routes created: ${url}`);
218
-
219
- // Detecting filters
220
- const middlewares = getModelMiddlewares(model, handler);
221
-
222
- // Adding created route to the documentation
223
- docs.push(routeTemplate.method, url, model);
224
-
225
- // Adding the route to the express
226
- app[routeTemplate.method.toLowerCase()](
227
- url,
228
- middlewares,
229
- (req, res, next) => {
230
- requestHandler(handler, req, res, next, context);
231
- }
232
- );
233
- }
234
-
235
- await createChildRoutes(model, models, resource, urlPrefix);
236
- await createNestedRoutes(model, models, allowRecursive, urlPrefix, resource);
237
- };
238
-
239
- const getGeneralHooks = async (appDirectory) => {
240
- const fs = await IoC.use("fs");
241
- const path = await IoC.use("path");
242
- const url = await IoC.use("url");
243
-
244
- // Calling the user's custom definitions
245
- const customInitFile = path.join(appDirectory, `init.js`);
246
- if (fs.existsSync(customInitFile)) {
247
- const { onBeforeInit, onAfterInit } = await import(
248
- url.pathToFileURL(customInitFile).href
249
- );
250
- return { onBeforeInit, onAfterInit };
251
- }
252
-
253
- return { onBeforeInit: null, onAfterInit: null };
254
- };
255
-
256
- const createRoutesByModelTree = async (modelTree, models) => {
257
- for (const model of modelTree) {
258
- await createRouteByModel(model, models);
259
- }
260
- };
261
-
262
- export default async (app, modelTree, appDirectory, models) => {
263
- Config = await IoC.use("Config");
264
- const logger = await IoC.use("Logger");
265
-
266
- const { onBeforeInit, onAfterInit } = await getGeneralHooks(appDirectory);
267
-
268
- if (typeof onBeforeInit === "function") {
269
- await onBeforeInit({ app });
270
- }
271
-
272
- await createRoutesByModelTree(modelTree, models);
273
-
274
- if (typeof onAfterInit === "function") {
275
- await onAfterInit({ app });
276
- }
277
-
278
- logger.info("All routes have been created.");
279
- };
@@ -1,25 +0,0 @@
1
- import IoC from "./../core/IoC.js";
2
- import { HOOK_FUNCTIONS } from "./../constants.js";
3
-
4
- export default async (type, appDirectory, models) => {
5
- const logger = await IoC.use("Logger");
6
- const fs = await IoC.use("fs");
7
- const path = await IoC.use("path");
8
- const url = await IoC.use("url");
9
-
10
- appDirectory = path.join(appDirectory, type);
11
- for (const model of models) {
12
- model[type.toLowerCase()] = {};
13
- const fileName = path.join(appDirectory, `${model.name}${type}.js`);
14
- if (fs.existsSync(fileName)) {
15
- const Hooks = await import(url.pathToFileURL(fileName).href);
16
- for (const hook of Object.keys(HOOK_FUNCTIONS)) {
17
- if (Hooks[hook]) {
18
- model[type.toLowerCase()][hook] = Hooks[hook];
19
- }
20
- }
21
- }
22
- }
23
-
24
- logger.info(`${type} have been mapped.`);
25
- };
@@ -1,41 +0,0 @@
1
- import IoC from "./../core/IoC.js";
2
- import { DEFAULT_METHODS_OF_MODELS } from "./../constants.js";
3
- import { paramCase } from "change-case";
4
-
5
- const getInstanceMethods = (obj) => {
6
- let properties = new Set();
7
- let currentObj = obj;
8
- do {
9
- Object.getOwnPropertyNames(currentObj).forEach((item) =>
10
- properties.add(item)
11
- );
12
- } while ((currentObj = Object.getPrototypeOf(currentObj)));
13
- return [...properties.keys()].filter(
14
- (item) => typeof obj[item] === "function"
15
- );
16
- };
17
-
18
- const getModelRelationMethods = (model) => {
19
- return getInstanceMethods(model.instance).filter(
20
- (method) => !DEFAULT_METHODS_OF_MODELS.includes(method)
21
- );
22
- };
23
-
24
- export default async (models) => {
25
- const logger = await IoC.use("Logger");
26
-
27
- for (const model of models) {
28
- const relationMethods = getModelRelationMethods(model);
29
-
30
- for (const relationMethod of relationMethods) {
31
- const relation = model.instance[relationMethod]();
32
- model.instance.relations.push({
33
- name: relationMethod,
34
- resource: paramCase(relationMethod).toLowerCase(),
35
- ...relation,
36
- });
37
- }
38
- }
39
-
40
- logger.info("All relationships have been resolved.");
41
- };