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.
@@ -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
+ }*/