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/handlers/update.js
DELETED
|
@@ -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
|
-
};
|
package/src/resolvers/index.js
DELETED
|
@@ -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,286 +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
|
-
export const getRootPrefix = async () => {
|
|
168
|
-
const config = await IoC.use("Config");
|
|
169
|
-
let prefix = config?.Application?.prefix || "api";
|
|
170
|
-
|
|
171
|
-
if (prefix.substr(0, 1) === "/") {
|
|
172
|
-
prefix = prefix.substr(1);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (prefix.substr(prefix.length - 1) === "/") {
|
|
176
|
-
prefix = prefix.substr(0, prefix.length - 1);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return prefix;
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const createRouteByModel = async (
|
|
183
|
-
model,
|
|
184
|
-
models,
|
|
185
|
-
urlPrefix = "",
|
|
186
|
-
parentModel = null,
|
|
187
|
-
relation = null,
|
|
188
|
-
allowRecursive = true
|
|
189
|
-
) => {
|
|
190
|
-
if (model.instance.ignore) {
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const logger = await IoC.use("Logger");
|
|
195
|
-
const app = await IoC.use("App");
|
|
196
|
-
const database = await IoC.use("Database");
|
|
197
|
-
const docs = await IoC.use("Docs");
|
|
198
|
-
|
|
199
|
-
const context = {
|
|
200
|
-
model,
|
|
201
|
-
models,
|
|
202
|
-
parentModel,
|
|
203
|
-
relation,
|
|
204
|
-
Config,
|
|
205
|
-
database,
|
|
206
|
-
logger,
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
const resource = getResourcePath(model, relation);
|
|
210
|
-
|
|
211
|
-
// We create and handle routes by not duplicate so many lines.
|
|
212
|
-
for (const handler of Object.keys(API_ROUTE_TEMPLATES)) {
|
|
213
|
-
if (!model.instance.handlers.includes(handler)) {
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const routeTemplate = API_ROUTE_TEMPLATES[handler];
|
|
218
|
-
const url = routeTemplate.url(
|
|
219
|
-
await getRootPrefix(),
|
|
220
|
-
urlPrefix,
|
|
221
|
-
resource,
|
|
222
|
-
model.instance.primaryKey
|
|
223
|
-
);
|
|
224
|
-
logger.debug(`Model routes created: ${url}`);
|
|
225
|
-
|
|
226
|
-
// Detecting filters
|
|
227
|
-
const middlewares = getModelMiddlewares(model, handler);
|
|
228
|
-
|
|
229
|
-
// Adding created route to the documentation
|
|
230
|
-
docs.push(routeTemplate.method, url, model);
|
|
231
|
-
|
|
232
|
-
// Adding the route to the express
|
|
233
|
-
app[routeTemplate.method.toLowerCase()](
|
|
234
|
-
url,
|
|
235
|
-
middlewares,
|
|
236
|
-
(req, res, next) => {
|
|
237
|
-
requestHandler(handler, req, res, next, context);
|
|
238
|
-
}
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
await createChildRoutes(model, models, resource, urlPrefix);
|
|
243
|
-
await createNestedRoutes(model, models, allowRecursive, urlPrefix, resource);
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
const getGeneralHooks = async (appDirectory) => {
|
|
247
|
-
const fs = await IoC.use("fs");
|
|
248
|
-
const path = await IoC.use("path");
|
|
249
|
-
const url = await IoC.use("url");
|
|
250
|
-
|
|
251
|
-
// Calling the user's custom definitions
|
|
252
|
-
const customInitFile = path.join(appDirectory, `init.js`);
|
|
253
|
-
if (fs.existsSync(customInitFile)) {
|
|
254
|
-
const { onBeforeInit, onAfterInit } = await import(
|
|
255
|
-
url.pathToFileURL(customInitFile).href
|
|
256
|
-
);
|
|
257
|
-
return { onBeforeInit, onAfterInit };
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return { onBeforeInit: null, onAfterInit: null };
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
const createRoutesByModelTree = async (modelTree, models) => {
|
|
264
|
-
for (const model of modelTree) {
|
|
265
|
-
await createRouteByModel(model, models);
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
export default async (app, modelTree, appDirectory, models) => {
|
|
270
|
-
Config = await IoC.use("Config");
|
|
271
|
-
const logger = await IoC.use("Logger");
|
|
272
|
-
|
|
273
|
-
const { onBeforeInit, onAfterInit } = await getGeneralHooks(appDirectory);
|
|
274
|
-
|
|
275
|
-
if (typeof onBeforeInit === "function") {
|
|
276
|
-
await onBeforeInit({ app });
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
await createRoutesByModelTree(modelTree, models);
|
|
280
|
-
|
|
281
|
-
if (typeof onAfterInit === "function") {
|
|
282
|
-
await onAfterInit({ app });
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
logger.info("All routes have been created.");
|
|
286
|
-
};
|
|
@@ -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
|
-
};
|