spice-js 2.7.33 → 2.7.35
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/docs/generator.js +9 -4
- package/build/utility/RestHelper.js +51 -33
- package/package.json +1 -1
- package/src/docs/generator.js +9 -4
- package/src/utility/RestHelper.js +97 -44
package/build/docs/generator.js
CHANGED
|
@@ -222,11 +222,16 @@ function _getModels() {
|
|
|
222
222
|
var models = {};
|
|
223
223
|
require("fs").readdirSync(paths).forEach(function (file) {
|
|
224
224
|
var model = require(_path2.default.join(paths, file));
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
|
|
225
|
+
// `props` is a named export; the default export is the SpiceModel class,
|
|
226
|
+
// which does not carry it. Unwrapping to the default export first drops
|
|
227
|
+
// the schema off every model and leaves Swagger with empty definitions,
|
|
228
|
+
// so read the named export and only fall back to the default export for
|
|
229
|
+
// models that attach props there instead.
|
|
230
|
+
var props = model.props;
|
|
231
|
+
if (props == undefined && model.default != undefined) {
|
|
232
|
+
props = model.default.props;
|
|
228
233
|
}
|
|
229
|
-
models[file.split(".")[0]] =
|
|
234
|
+
models[file.split(".")[0]] = props;
|
|
230
235
|
});
|
|
231
236
|
return models;
|
|
232
237
|
});
|
|
@@ -4,7 +4,6 @@ exports.__esModule = true;
|
|
|
4
4
|
exports.default = void 0;
|
|
5
5
|
var _lodash = _interopRequireDefault(require("lodash"));
|
|
6
6
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
-
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) { "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); } return f; })(e, t); }
|
|
8
7
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) { ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } } return n; }, _extends.apply(null, arguments); }
|
|
9
8
|
function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
|
|
10
9
|
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
@@ -37,7 +36,9 @@ class RestHelper {
|
|
|
37
36
|
ctx.status = 200;
|
|
38
37
|
ctx.body = response;
|
|
39
38
|
} catch (e) {
|
|
40
|
-
console.
|
|
39
|
+
console.error("Error at response: ", e, e == null ? void 0 : e.stack);
|
|
40
|
+
ctx.status = 500;
|
|
41
|
+
ctx.body = RestHelper.prepare_response(RestHelper.FAILURE, (e == null ? void 0 : e.message) || e);
|
|
41
42
|
}
|
|
42
43
|
})();
|
|
43
44
|
}
|
|
@@ -123,47 +124,65 @@ class RestHelper {
|
|
|
123
124
|
var filename, filePath;
|
|
124
125
|
if (download_type === "csv") {
|
|
125
126
|
var _ret = yield* function* () {
|
|
126
|
-
var {
|
|
127
|
-
flatten
|
|
128
|
-
} = yield Promise.resolve().then(() => _interopRequireWildcard(require("flat")));
|
|
129
127
|
var rows = Array.isArray(cleaned) ? cleaned : [cleaned];
|
|
130
128
|
var csvReady = rows.map(normalizeEmptyArraysForCsv);
|
|
131
|
-
var
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
var flattenToLeaves = function flattenToLeaves(value, prefix, output) {
|
|
130
|
+
if (prefix === void 0) {
|
|
131
|
+
prefix = "";
|
|
132
|
+
}
|
|
133
|
+
if (output === void 0) {
|
|
134
|
+
output = {};
|
|
135
|
+
}
|
|
136
|
+
if (Array.isArray(value)) {
|
|
137
|
+
if (value.length === 0 && prefix) {
|
|
138
|
+
output[prefix] = "";
|
|
139
|
+
return output;
|
|
140
|
+
}
|
|
141
|
+
value.forEach((item, index) => {
|
|
142
|
+
var path = prefix ? prefix + "." + index : "" + index;
|
|
143
|
+
flattenToLeaves(item, path, output);
|
|
144
|
+
});
|
|
145
|
+
return output;
|
|
146
|
+
}
|
|
147
|
+
if (value && typeof value === "object") {
|
|
148
|
+
var entries = Object.entries(value);
|
|
149
|
+
if (entries.length === 0 && prefix) {
|
|
150
|
+
output[prefix] = "";
|
|
151
|
+
return output;
|
|
152
|
+
}
|
|
153
|
+
for (var [key, item] of entries) {
|
|
154
|
+
var _path = prefix ? prefix + "." + key : key;
|
|
155
|
+
flattenToLeaves(item, _path, output);
|
|
156
|
+
}
|
|
157
|
+
return output;
|
|
158
|
+
}
|
|
159
|
+
if (prefix) {
|
|
160
|
+
output[prefix] = value;
|
|
161
|
+
}
|
|
162
|
+
return output;
|
|
163
|
+
};
|
|
164
|
+
var flatRows = csvReady.map(row => flattenToLeaves(row));
|
|
135
165
|
var fieldSet = new Set();
|
|
136
|
-
for (var
|
|
137
|
-
Object.keys(
|
|
166
|
+
for (var row of flatRows) {
|
|
167
|
+
Object.keys(row).forEach(field => fieldSet.add(field));
|
|
138
168
|
}
|
|
139
169
|
var fields = Array.from(fieldSet).sort((a, b) => a.localeCompare(b, undefined, {
|
|
140
170
|
numeric: true,
|
|
141
171
|
sensitivity: "base"
|
|
142
172
|
}));
|
|
143
|
-
|
|
144
|
-
// Filter flattened fields to only include requested columns
|
|
145
173
|
var rawUrl = ctx.originalUrl || ctx.request.url || "";
|
|
146
174
|
var urlColumnsMatch = rawUrl.match(/[?&]columns=([^&]*)/);
|
|
147
|
-
var requestedColumns = urlColumnsMatch ? decodeURIComponent(urlColumnsMatch[1]) : null;
|
|
175
|
+
var requestedColumns = urlColumnsMatch ? decodeURIComponent(urlColumnsMatch[1].replace(/\+/g, " ")) : null;
|
|
148
176
|
if (requestedColumns) {
|
|
149
|
-
var
|
|
150
|
-
var
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
var segments = spec.split(".");
|
|
159
|
-
var pattern = segments.map(s => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("(\\.\\d+)?\\.");
|
|
160
|
-
var re = new RegExp("^" + pattern + "(\\..+)?$");
|
|
161
|
-
for (var f of fields) {
|
|
162
|
-
if (re.test(f)) filtered.push(f);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
fields = filtered.length > 0 ? filtered : fields;
|
|
177
|
+
var requestedSpecs = requestedColumns.split(",").map(column => column.trim().replace(/`/g, "")).filter(Boolean);
|
|
178
|
+
var normalizePath = field => field.split(".").filter(segment => !/^\d+$/.test(segment)).join(".");
|
|
179
|
+
fields = fields.filter(field => {
|
|
180
|
+
var normalizedField = normalizePath(field);
|
|
181
|
+
return requestedSpecs.some(requestedField => {
|
|
182
|
+
var normalizedRequestedField = normalizePath(requestedField);
|
|
183
|
+
return normalizedField === normalizedRequestedField || normalizedField.startsWith(normalizedRequestedField + ".");
|
|
184
|
+
});
|
|
185
|
+
});
|
|
167
186
|
}
|
|
168
187
|
var csv = parse(flatRows, {
|
|
169
188
|
fields,
|
|
@@ -178,7 +197,6 @@ class RestHelper {
|
|
|
178
197
|
ctx.type = "text/csv; charset=utf-8";
|
|
179
198
|
ctx.status = 200;
|
|
180
199
|
var csvStream = fs.createReadStream(filePath);
|
|
181
|
-
// Delete file after stream is finished
|
|
182
200
|
csvStream.on("close", () => {
|
|
183
201
|
fs.promises.unlink(filePath).catch(() => {});
|
|
184
202
|
});
|
package/package.json
CHANGED
package/src/docs/generator.js
CHANGED
|
@@ -244,11 +244,16 @@ async function getModels() {
|
|
|
244
244
|
.readdirSync(paths)
|
|
245
245
|
.forEach(function (file) {
|
|
246
246
|
let model = require(path.join(paths, file));
|
|
247
|
-
//
|
|
248
|
-
|
|
249
|
-
|
|
247
|
+
// `props` is a named export; the default export is the SpiceModel class,
|
|
248
|
+
// which does not carry it. Unwrapping to the default export first drops
|
|
249
|
+
// the schema off every model and leaves Swagger with empty definitions,
|
|
250
|
+
// so read the named export and only fall back to the default export for
|
|
251
|
+
// models that attach props there instead.
|
|
252
|
+
let props = model.props;
|
|
253
|
+
if (props == undefined && model.default != undefined) {
|
|
254
|
+
props = model.default.props;
|
|
250
255
|
}
|
|
251
|
-
models[file.split(".")[0]] =
|
|
256
|
+
models[file.split(".")[0]] = props;
|
|
252
257
|
});
|
|
253
258
|
return models;
|
|
254
259
|
}
|
|
@@ -30,7 +30,12 @@ export default class RestHelper {
|
|
|
30
30
|
ctx.status = 200;
|
|
31
31
|
ctx.body = response;
|
|
32
32
|
} catch (e) {
|
|
33
|
-
console.
|
|
33
|
+
console.error("Error at response: ", e, e?.stack);
|
|
34
|
+
ctx.status = 500;
|
|
35
|
+
ctx.body = RestHelper.prepare_response(
|
|
36
|
+
RestHelper.FAILURE,
|
|
37
|
+
e?.message || e,
|
|
38
|
+
);
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
|
|
@@ -100,7 +105,6 @@ export default class RestHelper {
|
|
|
100
105
|
try {
|
|
101
106
|
const download_type = (ctx.request.query.format || "csv").toLowerCase();
|
|
102
107
|
const include_id = ctx.request.query.include_id;
|
|
103
|
-
|
|
104
108
|
const original = _.cloneDeep(ctx.data);
|
|
105
109
|
const trimmed = deepTrimStrings(original);
|
|
106
110
|
const cleaned = stripTopLevel(trimmed, {
|
|
@@ -108,78 +112,126 @@ export default class RestHelper {
|
|
|
108
112
|
});
|
|
109
113
|
|
|
110
114
|
let filename, filePath;
|
|
111
|
-
|
|
112
115
|
if (download_type === "csv") {
|
|
113
|
-
const { flatten } = await import("flat");
|
|
114
116
|
const rows = Array.isArray(cleaned) ? cleaned : [cleaned];
|
|
115
|
-
|
|
117
|
+
|
|
116
118
|
const csvReady = rows.map(normalizeEmptyArraysForCsv);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
|
|
120
|
+
const flattenToLeaves = (value, prefix = "", output = {}) => {
|
|
121
|
+
if (Array.isArray(value)) {
|
|
122
|
+
if (value.length === 0 && prefix) {
|
|
123
|
+
output[prefix] = "";
|
|
124
|
+
return output;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
value.forEach((item, index) => {
|
|
128
|
+
const path = prefix ? `${prefix}.${index}` : `${index}`;
|
|
129
|
+
flattenToLeaves(item, path, output);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return output;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (value && typeof value === "object") {
|
|
136
|
+
const entries = Object.entries(value);
|
|
137
|
+
|
|
138
|
+
if (entries.length === 0 && prefix) {
|
|
139
|
+
output[prefix] = "";
|
|
140
|
+
return output;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
for (const [key, item] of entries) {
|
|
144
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
145
|
+
flattenToLeaves(item, path, output);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return output;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (prefix) {
|
|
152
|
+
output[prefix] = value;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return output;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const flatRows = csvReady.map((row) => flattenToLeaves(row));
|
|
159
|
+
|
|
121
160
|
const fieldSet = new Set();
|
|
122
|
-
|
|
123
|
-
|
|
161
|
+
|
|
162
|
+
for (const row of flatRows) {
|
|
163
|
+
Object.keys(row).forEach((field) => fieldSet.add(field));
|
|
164
|
+
}
|
|
165
|
+
|
|
124
166
|
let fields = Array.from(fieldSet).sort((a, b) =>
|
|
125
|
-
a.localeCompare(b, undefined, {
|
|
167
|
+
a.localeCompare(b, undefined, {
|
|
168
|
+
numeric: true,
|
|
169
|
+
sensitivity: "base",
|
|
170
|
+
}),
|
|
126
171
|
);
|
|
127
|
-
|
|
128
|
-
// Filter flattened fields to only include requested columns
|
|
172
|
+
|
|
129
173
|
const rawUrl = ctx.originalUrl || ctx.request.url || "";
|
|
130
174
|
const urlColumnsMatch = rawUrl.match(/[?&]columns=([^&]*)/);
|
|
175
|
+
|
|
131
176
|
const requestedColumns = urlColumnsMatch
|
|
132
|
-
? decodeURIComponent(urlColumnsMatch[1])
|
|
177
|
+
? decodeURIComponent(urlColumnsMatch[1].replace(/\+/g, " "))
|
|
133
178
|
: null;
|
|
179
|
+
|
|
134
180
|
if (requestedColumns) {
|
|
135
|
-
const
|
|
181
|
+
const requestedSpecs = requestedColumns
|
|
136
182
|
.split(",")
|
|
137
|
-
.map((
|
|
138
|
-
.filter(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
183
|
+
.map((column) => column.trim().replace(/`/g, ""))
|
|
184
|
+
.filter(Boolean);
|
|
185
|
+
|
|
186
|
+
const normalizePath = (field) =>
|
|
187
|
+
field
|
|
188
|
+
.split(".")
|
|
189
|
+
.filter((segment) => !/^\d+$/.test(segment))
|
|
190
|
+
.join(".");
|
|
191
|
+
|
|
192
|
+
fields = fields.filter((field) => {
|
|
193
|
+
const normalizedField = normalizePath(field);
|
|
194
|
+
|
|
195
|
+
return requestedSpecs.some((requestedField) => {
|
|
196
|
+
const normalizedRequestedField = normalizePath(requestedField);
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
normalizedField === normalizedRequestedField ||
|
|
200
|
+
normalizedField.startsWith(`${normalizedRequestedField}.`)
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
158
204
|
}
|
|
159
|
-
|
|
205
|
+
|
|
160
206
|
const csv = parse(flatRows, {
|
|
161
207
|
fields,
|
|
162
208
|
defaultValue: "",
|
|
163
209
|
excelStrings: true,
|
|
164
210
|
});
|
|
165
|
-
|
|
166
|
-
makeDirectory(
|
|
211
|
+
|
|
212
|
+
makeDirectory("./storage/exports/csv");
|
|
213
|
+
|
|
167
214
|
filename = `${RestHelper.makeid(9)}.csv`;
|
|
168
215
|
filePath = path.resolve(`./storage/exports/csv/${filename}`);
|
|
216
|
+
|
|
169
217
|
await fs.promises.writeFile(filePath, csv, "utf8");
|
|
170
|
-
|
|
171
|
-
ctx.set(
|
|
218
|
+
|
|
219
|
+
ctx.set(
|
|
220
|
+
"Content-Disposition",
|
|
221
|
+
`attachment; filename="${filename}"`,
|
|
222
|
+
);
|
|
172
223
|
ctx.type = "text/csv; charset=utf-8";
|
|
173
224
|
ctx.status = 200;
|
|
225
|
+
|
|
174
226
|
const csvStream = fs.createReadStream(filePath);
|
|
175
|
-
|
|
227
|
+
|
|
176
228
|
csvStream.on("close", () => {
|
|
177
229
|
fs.promises.unlink(filePath).catch(() => {});
|
|
178
230
|
});
|
|
231
|
+
|
|
179
232
|
ctx.body = csvStream;
|
|
180
233
|
return;
|
|
181
234
|
}
|
|
182
|
-
|
|
183
235
|
const jsonText = safeJSONStringify(cleaned, 2);
|
|
184
236
|
|
|
185
237
|
makeDirectory(`./storage/exports/json`);
|
|
@@ -202,6 +254,7 @@ export default class RestHelper {
|
|
|
202
254
|
}
|
|
203
255
|
}
|
|
204
256
|
|
|
257
|
+
|
|
205
258
|
static makeid(length) {
|
|
206
259
|
var result = "";
|
|
207
260
|
var characters =
|