db-model-router 1.0.0
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/.env +7 -0
- package/LICENSE +201 -0
- package/README.md +505 -0
- package/docker-compose.yml +141 -0
- package/docs/README.md +208 -0
- package/docs/SKILL.md +202 -0
- package/docs/adapters/cockroachdb.md +49 -0
- package/docs/adapters/dynamodb.md +53 -0
- package/docs/adapters/mongodb.md +56 -0
- package/docs/adapters/mssql.md +55 -0
- package/docs/adapters/oracle.md +52 -0
- package/docs/adapters/postgres.md +50 -0
- package/docs/adapters/redis.md +53 -0
- package/docs/adapters/sqlite3.md +43 -0
- package/package.json +109 -0
- package/src/cli/generate-app.js +359 -0
- package/src/cli/generate-model.js +760 -0
- package/src/cli/generate-openapi.js +237 -0
- package/src/cli/generate-route.js +346 -0
- package/src/cockroachdb/db.js +563 -0
- package/src/commons/function.js +165 -0
- package/src/commons/model.js +444 -0
- package/src/commons/route.js +214 -0
- package/src/commons/validator.js +172 -0
- package/src/dynamodb/db.js +552 -0
- package/src/index.js +57 -0
- package/src/mongodb/db.js +381 -0
- package/src/mssql/db.js +461 -0
- package/src/mysql/db.js +527 -0
- package/src/oracle/db.js +855 -0
- package/src/oracle/sql_translator.js +406 -0
- package/src/postgres/db.js +666 -0
- package/src/postgres/ddl_translator.js +69 -0
- package/src/postgres/sql_translator.js +396 -0
- package/src/redis/db.js +448 -0
- package/src/serve.js +90 -0
- package/src/sqlite3/db.js +346 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
const express = (() => {
|
|
2
|
+
try {
|
|
3
|
+
return require("ultimate-express");
|
|
4
|
+
} catch (_) {
|
|
5
|
+
return require("express");
|
|
6
|
+
}
|
|
7
|
+
})();
|
|
8
|
+
const { errorResponse } = require("./validator");
|
|
9
|
+
const { toCSV, toXML, extractReservedParams, applySelect } = require("./model");
|
|
10
|
+
const _ = require("lodash");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Send response in the requested format (json, csv, xml).
|
|
14
|
+
*/
|
|
15
|
+
function sendFormatted(res, data, contentType) {
|
|
16
|
+
if (contentType === "csv") {
|
|
17
|
+
const rows = Array.isArray(data) ? data : data.data || [data];
|
|
18
|
+
res.setHeader("Content-Type", "text/csv");
|
|
19
|
+
return res.send(toCSV(rows));
|
|
20
|
+
}
|
|
21
|
+
if (contentType === "xml") {
|
|
22
|
+
const rows = Array.isArray(data) ? data : data.data || [data];
|
|
23
|
+
res.setHeader("Content-Type", "application/xml");
|
|
24
|
+
return res.send(toXML(rows));
|
|
25
|
+
}
|
|
26
|
+
return res.status(200).send(data);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = function route(model, override = {}) {
|
|
30
|
+
return express
|
|
31
|
+
.Router({ mergeParams: true })
|
|
32
|
+
.get("/:" + model.pk, (req, res) => {
|
|
33
|
+
let payload = payloadOverride(
|
|
34
|
+
{ ...req.query, ...req.params },
|
|
35
|
+
req,
|
|
36
|
+
override,
|
|
37
|
+
);
|
|
38
|
+
const { select_columns, output_content_type } =
|
|
39
|
+
extractReservedParams(payload);
|
|
40
|
+
payload[model.pk] = req.params[model.pk];
|
|
41
|
+
model
|
|
42
|
+
.find(payload)
|
|
43
|
+
.then((response) => {
|
|
44
|
+
if (response.count > 0) {
|
|
45
|
+
let record = response.data[0];
|
|
46
|
+
if (select_columns) record = applySelect(record, select_columns);
|
|
47
|
+
sendFormatted(res, record, output_content_type);
|
|
48
|
+
} else res.status(404).send({ message: "Not Found", type: "danger" });
|
|
49
|
+
})
|
|
50
|
+
.catch((err) => {
|
|
51
|
+
errorResponse(res, err);
|
|
52
|
+
});
|
|
53
|
+
})
|
|
54
|
+
.post("/:id", (req, res) => {
|
|
55
|
+
let payload = payloadOverride(req.body, req, override);
|
|
56
|
+
delete payload[model.pk];
|
|
57
|
+
model
|
|
58
|
+
.insert(payload)
|
|
59
|
+
.then((response) => {
|
|
60
|
+
res.status(200).send(response);
|
|
61
|
+
})
|
|
62
|
+
.catch((err) => {
|
|
63
|
+
errorResponse(res, err);
|
|
64
|
+
});
|
|
65
|
+
})
|
|
66
|
+
.put("/:id", (req, res) => {
|
|
67
|
+
let payload = payloadOverride(req.body, req, override);
|
|
68
|
+
payload[model.pk] = req.params.id;
|
|
69
|
+
let validateAccessPayload = payloadOverride({}, req, override);
|
|
70
|
+
validateAccessPayload[model.pk] = req.params.id;
|
|
71
|
+
model
|
|
72
|
+
.findOne(validateAccessPayload)
|
|
73
|
+
.then((found) => {
|
|
74
|
+
if (found) {
|
|
75
|
+
model
|
|
76
|
+
.update(payload)
|
|
77
|
+
.then((response) => {
|
|
78
|
+
res.status(200).send(response);
|
|
79
|
+
})
|
|
80
|
+
.catch((err) => {
|
|
81
|
+
errorResponse(res, err);
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
res.status(404).send({ message: "Not Found", type: "danger" });
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
.catch((err) => {
|
|
88
|
+
errorResponse(res, err);
|
|
89
|
+
});
|
|
90
|
+
})
|
|
91
|
+
.patch("/:id", (req, res) => {
|
|
92
|
+
let payload = payloadOverride(req.body, req, override);
|
|
93
|
+
payload[model.pk] = req.params.id;
|
|
94
|
+
let validateAccessPayload = payloadOverride({}, req, override);
|
|
95
|
+
validateAccessPayload[model.pk] = req.params.id;
|
|
96
|
+
model
|
|
97
|
+
.findOne(validateAccessPayload)
|
|
98
|
+
.then((found) => {
|
|
99
|
+
if (found) {
|
|
100
|
+
model
|
|
101
|
+
.patch(payload)
|
|
102
|
+
.then((response) => {
|
|
103
|
+
res.status(200).send(response);
|
|
104
|
+
})
|
|
105
|
+
.catch((err) => {
|
|
106
|
+
errorResponse(res, err);
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
res.status(404).send({ message: "Not Found", type: "danger" });
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
.catch((err) => {
|
|
113
|
+
errorResponse(res, err);
|
|
114
|
+
});
|
|
115
|
+
})
|
|
116
|
+
.delete("/:id", (req, res) => {
|
|
117
|
+
let payload = payloadOverride(req.body, req, override);
|
|
118
|
+
payload[model.pk] = req.params.id;
|
|
119
|
+
let validateAccessPayload = payloadOverride({}, req, override);
|
|
120
|
+
validateAccessPayload[model.pk] = req.params.id;
|
|
121
|
+
model
|
|
122
|
+
.findOne(validateAccessPayload)
|
|
123
|
+
.then((found) => {
|
|
124
|
+
if (found) {
|
|
125
|
+
model
|
|
126
|
+
.remove(payload)
|
|
127
|
+
.then((response) => {
|
|
128
|
+
res.status(200).send(response);
|
|
129
|
+
})
|
|
130
|
+
.catch((err) => {
|
|
131
|
+
errorResponse(res, err);
|
|
132
|
+
});
|
|
133
|
+
} else {
|
|
134
|
+
res.status(404).send({ message: "Not Found", type: "danger" });
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
.catch((err) => {
|
|
138
|
+
errorResponse(res, err);
|
|
139
|
+
});
|
|
140
|
+
})
|
|
141
|
+
.get("/", (req, res) => {
|
|
142
|
+
let payload = payloadOverride(
|
|
143
|
+
{ ...req.query, ...req.params },
|
|
144
|
+
req,
|
|
145
|
+
override,
|
|
146
|
+
);
|
|
147
|
+
const { output_content_type } = extractReservedParams(payload);
|
|
148
|
+
// select_columns stays in payload — model.list handles it
|
|
149
|
+
model
|
|
150
|
+
.list(payload)
|
|
151
|
+
.then((response) => {
|
|
152
|
+
sendFormatted(res, response, output_content_type);
|
|
153
|
+
})
|
|
154
|
+
.catch((err) => {
|
|
155
|
+
errorResponse(res, err);
|
|
156
|
+
});
|
|
157
|
+
})
|
|
158
|
+
.post("/", (req, res) => {
|
|
159
|
+
let payload = payloadOverride(req.body.data, req, override);
|
|
160
|
+
model
|
|
161
|
+
.insert({ data: payload })
|
|
162
|
+
.then((response) => {
|
|
163
|
+
res.status(200).send(response);
|
|
164
|
+
})
|
|
165
|
+
.catch((err) => {
|
|
166
|
+
errorResponse(res, err);
|
|
167
|
+
});
|
|
168
|
+
})
|
|
169
|
+
.put("/", (req, res) => {
|
|
170
|
+
let payload = payloadOverride(req.body.data, req, override);
|
|
171
|
+
model
|
|
172
|
+
.update({ data: payload })
|
|
173
|
+
.then((response) => {
|
|
174
|
+
res.status(200).send(response);
|
|
175
|
+
})
|
|
176
|
+
.catch((err) => {
|
|
177
|
+
errorResponse(res, err);
|
|
178
|
+
});
|
|
179
|
+
})
|
|
180
|
+
.delete("/", (req, res) => {
|
|
181
|
+
let payload = payloadOverride(req.body.data, req, override);
|
|
182
|
+
model
|
|
183
|
+
.remove(payload)
|
|
184
|
+
.then((response) => {
|
|
185
|
+
res.status(200).send(response);
|
|
186
|
+
})
|
|
187
|
+
.catch((err) => {
|
|
188
|
+
errorResponse(res, err);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
function payloadOverride(payload, req, override) {
|
|
194
|
+
if (Array.isArray(payload)) {
|
|
195
|
+
for (const i in payload) {
|
|
196
|
+
payload[i] = dataOverride(payload[i], req, override);
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
payload = dataOverride(payload, req, override);
|
|
200
|
+
}
|
|
201
|
+
return payload;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function dataOverride(payload, req, override) {
|
|
205
|
+
for (const key in override) {
|
|
206
|
+
payload[key] = _.get(req, override[key], "");
|
|
207
|
+
}
|
|
208
|
+
for (const key in payload) {
|
|
209
|
+
if (payload[key] === "null") {
|
|
210
|
+
delete payload[key];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return payload;
|
|
214
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
const { Validator } = require("node-input-validator");
|
|
2
|
+
const { getType } = require("./function");
|
|
3
|
+
function RemovePK(modelPK, data) {
|
|
4
|
+
for (const item of data) {
|
|
5
|
+
if (item.hasOwnProperty(modelPK)) {
|
|
6
|
+
delete item[modelPK];
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function RemoveUnknownData(ModelStructure, data) {
|
|
11
|
+
const modelStructure = Object.keys(ModelStructure);
|
|
12
|
+
for (const item of data) {
|
|
13
|
+
for (const i in item) {
|
|
14
|
+
if (!modelStructure.includes(i)) {
|
|
15
|
+
delete item[i];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return data;
|
|
20
|
+
}
|
|
21
|
+
function getPayloadValidator(type, structure, pk, bulk = false) {
|
|
22
|
+
if (bulk) {
|
|
23
|
+
const body = {};
|
|
24
|
+
switch (type) {
|
|
25
|
+
case "CREATE":
|
|
26
|
+
body["data"] = "required|array";
|
|
27
|
+
for (const i in structure) {
|
|
28
|
+
if (i !== pk) body[`data.*.${i}`] = structure[i];
|
|
29
|
+
}
|
|
30
|
+
break;
|
|
31
|
+
case "UPDATE":
|
|
32
|
+
body["data"] = "required|array";
|
|
33
|
+
for (const i in structure) {
|
|
34
|
+
body[`data.*.${i}`] = structure[i];
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
case "DELETE":
|
|
38
|
+
body["filter"] = "required|array";
|
|
39
|
+
break;
|
|
40
|
+
default:
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
return body;
|
|
44
|
+
} else {
|
|
45
|
+
const validator = { body: {} };
|
|
46
|
+
switch (type) {
|
|
47
|
+
case "CREATE":
|
|
48
|
+
for (const i in structure) {
|
|
49
|
+
if (i !== pk) validator.body[i] = structure[i];
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
case "UPDATE":
|
|
53
|
+
for (const i in structure) {
|
|
54
|
+
validator.body[i] = structure[i];
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
case "DELETE":
|
|
58
|
+
case "GET":
|
|
59
|
+
validator.body[pk] = structure[pk];
|
|
60
|
+
break;
|
|
61
|
+
default:
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
return validator.body;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function errorResponse(res, err) {
|
|
68
|
+
let status = 500;
|
|
69
|
+
if (err.hasOwnProperty("cause") && err.cause.hasOwnProperty("status")) {
|
|
70
|
+
status = err.cause.status;
|
|
71
|
+
}
|
|
72
|
+
// For client errors (4xx), return the validation message.
|
|
73
|
+
// For server/database errors (5xx), return a generic message to avoid leaking internals.
|
|
74
|
+
let message;
|
|
75
|
+
if (status >= 400 && status < 500) {
|
|
76
|
+
message = err.message || "Bad request";
|
|
77
|
+
} else {
|
|
78
|
+
if (
|
|
79
|
+
process.env.NODE_ENV === "development" ||
|
|
80
|
+
process.env.NODE_ENV === "test" ||
|
|
81
|
+
process.env.NODE_ENV === "TEST"
|
|
82
|
+
) {
|
|
83
|
+
message = err.sqlMessage || err.message || "Internal server error";
|
|
84
|
+
} else {
|
|
85
|
+
message = "Internal server error";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
res.status(status).send({ type: "danger", message });
|
|
89
|
+
}
|
|
90
|
+
async function validateInput(req, required) {
|
|
91
|
+
let validator = new Validator(req, required);
|
|
92
|
+
const matched = await validator.check();
|
|
93
|
+
if (!matched) {
|
|
94
|
+
throw new Error(getErrorMessage(validator.errors), {
|
|
95
|
+
cause: { status: 422 },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
function getErrorMessage(errors) {
|
|
101
|
+
let message = "";
|
|
102
|
+
for (const i in errors) {
|
|
103
|
+
if (errors.hasOwnProperty(i)) {
|
|
104
|
+
message = message + (message !== "" ? " " : "");
|
|
105
|
+
message = message + errors[i].message;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return message;
|
|
109
|
+
}
|
|
110
|
+
function objectToFilter(obj) {
|
|
111
|
+
let filterArray = [];
|
|
112
|
+
for (let key in obj) {
|
|
113
|
+
filterArray.push([key, "=", obj[key]]);
|
|
114
|
+
}
|
|
115
|
+
return [filterArray];
|
|
116
|
+
}
|
|
117
|
+
function dataToFilter(data, primary_key) {
|
|
118
|
+
let filter = [];
|
|
119
|
+
let type = getType(data);
|
|
120
|
+
if (data.hasOwnProperty("filter") && getType(data.filter) === "array") {
|
|
121
|
+
filter = JSON.parse(JSON.stringify(data.filter));
|
|
122
|
+
if (Object.keys(data).length > 1) {
|
|
123
|
+
delete data.filter;
|
|
124
|
+
let filter2 = objectToFilter(data);
|
|
125
|
+
if (filter.toString().length > 0) {
|
|
126
|
+
for (let item1 of filter) {
|
|
127
|
+
for (let item2 of filter2[0]) {
|
|
128
|
+
item1.push(item2);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
filter = filter2;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else if (type === "object" && Object.keys(data).length > 0) {
|
|
136
|
+
filter = objectToFilter(data);
|
|
137
|
+
} else if (type === "number" || type === "string") {
|
|
138
|
+
filter = [[[primary_key, "=", data]]];
|
|
139
|
+
} else if (type === "object" && Object.keys(data).length === 0) {
|
|
140
|
+
filter = [[[]]];
|
|
141
|
+
} else {
|
|
142
|
+
throw new Error("Invalid filter Inputs", { cause: { status: 422 } });
|
|
143
|
+
}
|
|
144
|
+
return filter;
|
|
145
|
+
}
|
|
146
|
+
module.exports = {
|
|
147
|
+
RemovePK,
|
|
148
|
+
RemoveUnknownData,
|
|
149
|
+
getPayloadValidator,
|
|
150
|
+
errorResponse,
|
|
151
|
+
validateInput,
|
|
152
|
+
getErrorMessage,
|
|
153
|
+
objectToFilter,
|
|
154
|
+
dataToFilter,
|
|
155
|
+
};
|
|
156
|
+
/*async function validateInput(req, required) {
|
|
157
|
+
console.log(req, required);
|
|
158
|
+
for (const key in required) {
|
|
159
|
+
if (req.hasOwnProperty(key)) {
|
|
160
|
+
if (Array.isArray(req[key])) {
|
|
161
|
+
throw Error({ message: "This service supports does not support array", status: 422 });
|
|
162
|
+
}
|
|
163
|
+
let validator = new Validator(req[key], required[key]);
|
|
164
|
+
const matched = await validator.check();
|
|
165
|
+
if (!matched) {
|
|
166
|
+
console.log(getErrorMessage(key, validator.errors));
|
|
167
|
+
//throw Error({ message: getErrorMessage(key, validator.errors), status: 422 });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
}*/
|