spice-js 2.7.33 → 2.7.34
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/utility/RestHelper.js +48 -32
- package/package.json +1 -1
- package/src/utility/RestHelper.js +91 -43
|
@@ -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); }); }; }
|
|
@@ -123,47 +122,65 @@ class RestHelper {
|
|
|
123
122
|
var filename, filePath;
|
|
124
123
|
if (download_type === "csv") {
|
|
125
124
|
var _ret = yield* function* () {
|
|
126
|
-
var {
|
|
127
|
-
flatten
|
|
128
|
-
} = yield Promise.resolve().then(() => _interopRequireWildcard(require("flat")));
|
|
129
125
|
var rows = Array.isArray(cleaned) ? cleaned : [cleaned];
|
|
130
126
|
var csvReady = rows.map(normalizeEmptyArraysForCsv);
|
|
131
|
-
var
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
127
|
+
var flattenToLeaves = function flattenToLeaves(value, prefix, output) {
|
|
128
|
+
if (prefix === void 0) {
|
|
129
|
+
prefix = "";
|
|
130
|
+
}
|
|
131
|
+
if (output === void 0) {
|
|
132
|
+
output = {};
|
|
133
|
+
}
|
|
134
|
+
if (Array.isArray(value)) {
|
|
135
|
+
if (value.length === 0 && prefix) {
|
|
136
|
+
output[prefix] = "";
|
|
137
|
+
return output;
|
|
138
|
+
}
|
|
139
|
+
value.forEach((item, index) => {
|
|
140
|
+
var path = prefix ? prefix + "." + index : "" + index;
|
|
141
|
+
flattenToLeaves(item, path, output);
|
|
142
|
+
});
|
|
143
|
+
return output;
|
|
144
|
+
}
|
|
145
|
+
if (value && typeof value === "object") {
|
|
146
|
+
var entries = Object.entries(value);
|
|
147
|
+
if (entries.length === 0 && prefix) {
|
|
148
|
+
output[prefix] = "";
|
|
149
|
+
return output;
|
|
150
|
+
}
|
|
151
|
+
for (var [key, item] of entries) {
|
|
152
|
+
var _path = prefix ? prefix + "." + key : key;
|
|
153
|
+
flattenToLeaves(item, _path, output);
|
|
154
|
+
}
|
|
155
|
+
return output;
|
|
156
|
+
}
|
|
157
|
+
if (prefix) {
|
|
158
|
+
output[prefix] = value;
|
|
159
|
+
}
|
|
160
|
+
return output;
|
|
161
|
+
};
|
|
162
|
+
var flatRows = csvReady.map(row => flattenToLeaves(row));
|
|
135
163
|
var fieldSet = new Set();
|
|
136
|
-
for (var
|
|
137
|
-
Object.keys(
|
|
164
|
+
for (var row of flatRows) {
|
|
165
|
+
Object.keys(row).forEach(field => fieldSet.add(field));
|
|
138
166
|
}
|
|
139
167
|
var fields = Array.from(fieldSet).sort((a, b) => a.localeCompare(b, undefined, {
|
|
140
168
|
numeric: true,
|
|
141
169
|
sensitivity: "base"
|
|
142
170
|
}));
|
|
143
|
-
|
|
144
|
-
// Filter flattened fields to only include requested columns
|
|
145
171
|
var rawUrl = ctx.originalUrl || ctx.request.url || "";
|
|
146
172
|
var urlColumnsMatch = rawUrl.match(/[?&]columns=([^&]*)/);
|
|
147
|
-
var requestedColumns = urlColumnsMatch ? decodeURIComponent(urlColumnsMatch[1]) : null;
|
|
173
|
+
var requestedColumns = urlColumnsMatch ? decodeURIComponent(urlColumnsMatch[1].replace(/\+/g, " ")) : null;
|
|
148
174
|
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;
|
|
175
|
+
var requestedSpecs = requestedColumns.split(",").map(column => column.trim().replace(/`/g, "")).filter(Boolean);
|
|
176
|
+
var normalizePath = field => field.split(".").filter(segment => !/^\d+$/.test(segment)).join(".");
|
|
177
|
+
fields = fields.filter(field => {
|
|
178
|
+
var normalizedField = normalizePath(field);
|
|
179
|
+
return requestedSpecs.some(requestedField => {
|
|
180
|
+
var normalizedRequestedField = normalizePath(requestedField);
|
|
181
|
+
return normalizedField === normalizedRequestedField || normalizedField.startsWith(normalizedRequestedField + ".");
|
|
182
|
+
});
|
|
183
|
+
});
|
|
167
184
|
}
|
|
168
185
|
var csv = parse(flatRows, {
|
|
169
186
|
fields,
|
|
@@ -178,7 +195,6 @@ class RestHelper {
|
|
|
178
195
|
ctx.type = "text/csv; charset=utf-8";
|
|
179
196
|
ctx.status = 200;
|
|
180
197
|
var csvStream = fs.createReadStream(filePath);
|
|
181
|
-
// Delete file after stream is finished
|
|
182
198
|
csvStream.on("close", () => {
|
|
183
199
|
fs.promises.unlink(filePath).catch(() => {});
|
|
184
200
|
});
|
package/package.json
CHANGED
|
@@ -100,7 +100,6 @@ export default class RestHelper {
|
|
|
100
100
|
try {
|
|
101
101
|
const download_type = (ctx.request.query.format || "csv").toLowerCase();
|
|
102
102
|
const include_id = ctx.request.query.include_id;
|
|
103
|
-
|
|
104
103
|
const original = _.cloneDeep(ctx.data);
|
|
105
104
|
const trimmed = deepTrimStrings(original);
|
|
106
105
|
const cleaned = stripTopLevel(trimmed, {
|
|
@@ -108,78 +107,126 @@ export default class RestHelper {
|
|
|
108
107
|
});
|
|
109
108
|
|
|
110
109
|
let filename, filePath;
|
|
111
|
-
|
|
112
110
|
if (download_type === "csv") {
|
|
113
|
-
const { flatten } = await import("flat");
|
|
114
111
|
const rows = Array.isArray(cleaned) ? cleaned : [cleaned];
|
|
115
|
-
|
|
112
|
+
|
|
116
113
|
const csvReady = rows.map(normalizeEmptyArraysForCsv);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
|
|
115
|
+
const flattenToLeaves = (value, prefix = "", output = {}) => {
|
|
116
|
+
if (Array.isArray(value)) {
|
|
117
|
+
if (value.length === 0 && prefix) {
|
|
118
|
+
output[prefix] = "";
|
|
119
|
+
return output;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
value.forEach((item, index) => {
|
|
123
|
+
const path = prefix ? `${prefix}.${index}` : `${index}`;
|
|
124
|
+
flattenToLeaves(item, path, output);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return output;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (value && typeof value === "object") {
|
|
131
|
+
const entries = Object.entries(value);
|
|
132
|
+
|
|
133
|
+
if (entries.length === 0 && prefix) {
|
|
134
|
+
output[prefix] = "";
|
|
135
|
+
return output;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for (const [key, item] of entries) {
|
|
139
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
140
|
+
flattenToLeaves(item, path, output);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return output;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (prefix) {
|
|
147
|
+
output[prefix] = value;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return output;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const flatRows = csvReady.map((row) => flattenToLeaves(row));
|
|
154
|
+
|
|
121
155
|
const fieldSet = new Set();
|
|
122
|
-
|
|
123
|
-
|
|
156
|
+
|
|
157
|
+
for (const row of flatRows) {
|
|
158
|
+
Object.keys(row).forEach((field) => fieldSet.add(field));
|
|
159
|
+
}
|
|
160
|
+
|
|
124
161
|
let fields = Array.from(fieldSet).sort((a, b) =>
|
|
125
|
-
a.localeCompare(b, undefined, {
|
|
162
|
+
a.localeCompare(b, undefined, {
|
|
163
|
+
numeric: true,
|
|
164
|
+
sensitivity: "base",
|
|
165
|
+
}),
|
|
126
166
|
);
|
|
127
|
-
|
|
128
|
-
// Filter flattened fields to only include requested columns
|
|
167
|
+
|
|
129
168
|
const rawUrl = ctx.originalUrl || ctx.request.url || "";
|
|
130
169
|
const urlColumnsMatch = rawUrl.match(/[?&]columns=([^&]*)/);
|
|
170
|
+
|
|
131
171
|
const requestedColumns = urlColumnsMatch
|
|
132
|
-
? decodeURIComponent(urlColumnsMatch[1])
|
|
172
|
+
? decodeURIComponent(urlColumnsMatch[1].replace(/\+/g, " "))
|
|
133
173
|
: null;
|
|
174
|
+
|
|
134
175
|
if (requestedColumns) {
|
|
135
|
-
const
|
|
176
|
+
const requestedSpecs = requestedColumns
|
|
136
177
|
.split(",")
|
|
137
|
-
.map((
|
|
138
|
-
.filter(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
178
|
+
.map((column) => column.trim().replace(/`/g, ""))
|
|
179
|
+
.filter(Boolean);
|
|
180
|
+
|
|
181
|
+
const normalizePath = (field) =>
|
|
182
|
+
field
|
|
183
|
+
.split(".")
|
|
184
|
+
.filter((segment) => !/^\d+$/.test(segment))
|
|
185
|
+
.join(".");
|
|
186
|
+
|
|
187
|
+
fields = fields.filter((field) => {
|
|
188
|
+
const normalizedField = normalizePath(field);
|
|
189
|
+
|
|
190
|
+
return requestedSpecs.some((requestedField) => {
|
|
191
|
+
const normalizedRequestedField = normalizePath(requestedField);
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
normalizedField === normalizedRequestedField ||
|
|
195
|
+
normalizedField.startsWith(`${normalizedRequestedField}.`)
|
|
196
|
+
);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
158
199
|
}
|
|
159
|
-
|
|
200
|
+
|
|
160
201
|
const csv = parse(flatRows, {
|
|
161
202
|
fields,
|
|
162
203
|
defaultValue: "",
|
|
163
204
|
excelStrings: true,
|
|
164
205
|
});
|
|
165
|
-
|
|
166
|
-
makeDirectory(
|
|
206
|
+
|
|
207
|
+
makeDirectory("./storage/exports/csv");
|
|
208
|
+
|
|
167
209
|
filename = `${RestHelper.makeid(9)}.csv`;
|
|
168
210
|
filePath = path.resolve(`./storage/exports/csv/${filename}`);
|
|
211
|
+
|
|
169
212
|
await fs.promises.writeFile(filePath, csv, "utf8");
|
|
170
|
-
|
|
171
|
-
ctx.set(
|
|
213
|
+
|
|
214
|
+
ctx.set(
|
|
215
|
+
"Content-Disposition",
|
|
216
|
+
`attachment; filename="${filename}"`,
|
|
217
|
+
);
|
|
172
218
|
ctx.type = "text/csv; charset=utf-8";
|
|
173
219
|
ctx.status = 200;
|
|
220
|
+
|
|
174
221
|
const csvStream = fs.createReadStream(filePath);
|
|
175
|
-
|
|
222
|
+
|
|
176
223
|
csvStream.on("close", () => {
|
|
177
224
|
fs.promises.unlink(filePath).catch(() => {});
|
|
178
225
|
});
|
|
226
|
+
|
|
179
227
|
ctx.body = csvStream;
|
|
180
228
|
return;
|
|
181
229
|
}
|
|
182
|
-
|
|
183
230
|
const jsonText = safeJSONStringify(cleaned, 2);
|
|
184
231
|
|
|
185
232
|
makeDirectory(`./storage/exports/json`);
|
|
@@ -202,6 +249,7 @@ export default class RestHelper {
|
|
|
202
249
|
}
|
|
203
250
|
}
|
|
204
251
|
|
|
252
|
+
|
|
205
253
|
static makeid(length) {
|
|
206
254
|
var result = "";
|
|
207
255
|
var characters =
|