spice-js 2.6.66 → 2.6.68
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/models/SpiceModel.js +28 -14
- package/build/utility/RestHelper.js +138 -37
- package/package.json +1 -1
- package/src/models/SpiceModel.js +74 -55
- package/src/utility/RestHelper.js +99 -43
|
@@ -932,6 +932,17 @@ class SpiceModel {
|
|
|
932
932
|
}
|
|
933
933
|
|
|
934
934
|
if (!columns || columns === "") return columns;
|
|
935
|
+
|
|
936
|
+
var q = function q(s) {
|
|
937
|
+
if (s === void 0) {
|
|
938
|
+
s = "";
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
var t = (s || "").trim();
|
|
942
|
+
if (t.startsWith("`") && t.endsWith("`")) return t;
|
|
943
|
+
return "`" + t.replace(/`/g, "``") + "`";
|
|
944
|
+
};
|
|
945
|
+
|
|
935
946
|
var protectedSet = new Set(protectedAliases);
|
|
936
947
|
var arraySet = new Set(arrayAliases);
|
|
937
948
|
var tokens = columns.split(",");
|
|
@@ -950,37 +961,40 @@ class SpiceModel {
|
|
|
950
961
|
var proj = this.buildArrayProjection(alias, field);
|
|
951
962
|
|
|
952
963
|
if (explicitAs && explicitAs !== alias + "_" + field) {
|
|
953
|
-
proj = proj.replace(/AS\s+`[^`]+`$/i, "AS
|
|
964
|
+
proj = proj.replace(/AS\s+`[^`]+`$/i, "AS " + q(explicitAs));
|
|
954
965
|
}
|
|
955
966
|
|
|
956
967
|
return proj;
|
|
957
968
|
}
|
|
958
969
|
|
|
959
970
|
if (protectedSet.has(alias)) {
|
|
960
|
-
var aliased =
|
|
961
|
-
return explicitAs ? aliased + " AS
|
|
971
|
+
var aliased = q(alias) + "." + (field.startsWith("`") ? field : q(field));
|
|
972
|
+
return explicitAs ? aliased + " AS " + q(explicitAs) : aliased;
|
|
962
973
|
}
|
|
963
974
|
|
|
964
|
-
var
|
|
965
|
-
return
|
|
975
|
+
var qualified = q(this.type) + "." + q(alias) + "." + q(field);
|
|
976
|
+
return explicitAs ? qualified + " AS " + q(explicitAs) : qualified;
|
|
966
977
|
}
|
|
967
978
|
|
|
968
|
-
var looksProtected = [...protectedSet].some(a => col === a || col ===
|
|
979
|
+
var looksProtected = [...protectedSet].some(a => col === a || col === q(a) || col.startsWith(a + ".") || col.startsWith(q(a) + "."));
|
|
969
980
|
|
|
970
981
|
if (!looksProtected) {
|
|
971
|
-
if (!col.
|
|
972
|
-
|
|
982
|
+
if (!col.includes(".")) {
|
|
983
|
+
var fieldQuoted = col.startsWith("`") ? col : q(col);
|
|
984
|
+
return q(this.type) + "." + fieldQuoted;
|
|
973
985
|
}
|
|
974
986
|
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
987
|
+
var parts = col.split(".");
|
|
988
|
+
var safeParts = parts.map(p => {
|
|
989
|
+
var t = p.trim();
|
|
990
|
+
if (t === "" || /\w+\s*\(/.test(t)) return t;
|
|
991
|
+
return t.startsWith("`") ? t : q(t);
|
|
992
|
+
});
|
|
993
|
+
return q(this.type) + "." + safeParts.join(".");
|
|
980
994
|
}
|
|
981
995
|
|
|
982
996
|
if (!col.includes(".") && !col.startsWith("`")) {
|
|
983
|
-
return
|
|
997
|
+
return q(col);
|
|
984
998
|
}
|
|
985
999
|
|
|
986
1000
|
return col;
|
|
@@ -11,6 +11,8 @@ function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return
|
|
|
11
11
|
|
|
12
12
|
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
13
13
|
|
|
14
|
+
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
15
|
+
|
|
14
16
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
15
17
|
|
|
16
18
|
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
@@ -66,55 +68,154 @@ class RestHelper {
|
|
|
66
68
|
|
|
67
69
|
static send_download(ctx, next) {
|
|
68
70
|
return _asyncToGenerator(function* () {
|
|
69
|
-
|
|
70
|
-
if (!fs.existsSync(dir)) {
|
|
71
|
-
|
|
71
|
+
var makeDirectory = dir => {
|
|
72
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, {
|
|
73
|
+
recursive: true
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
var deepTrimStrings = val => {
|
|
78
|
+
if (Array.isArray(val)) return val.map(deepTrimStrings);
|
|
79
|
+
|
|
80
|
+
if (val && typeof val === "object") {
|
|
81
|
+
var out = {};
|
|
82
|
+
|
|
83
|
+
for (var [k, v] of Object.entries(val)) {
|
|
84
|
+
out[k] = deepTrimStrings(v);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return out;
|
|
72
88
|
}
|
|
73
|
-
}
|
|
74
89
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
var include_id = ctx.request.query.include_id;
|
|
78
|
-
var content;
|
|
90
|
+
return typeof val === "string" ? val.trim() : val;
|
|
91
|
+
};
|
|
79
92
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
var stripTopLevel = (obj, _ref) => {
|
|
94
|
+
var {
|
|
95
|
+
removeId
|
|
96
|
+
} = _ref;
|
|
97
|
+
if (Array.isArray(obj)) return obj.map(i => stripTopLevel(i, {
|
|
98
|
+
removeId
|
|
99
|
+
}));
|
|
84
100
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
delete item.id;
|
|
88
|
-
}
|
|
101
|
+
if (obj && typeof obj === "object") {
|
|
102
|
+
var copy = _extends({}, obj);
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
if (removeId) delete copy.id;
|
|
105
|
+
delete copy._permissions_;
|
|
106
|
+
return copy;
|
|
107
|
+
}
|
|
93
108
|
|
|
94
|
-
|
|
109
|
+
return obj;
|
|
110
|
+
};
|
|
95
111
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
ctx.set("content-type", "text/csv");
|
|
101
|
-
} else {
|
|
102
|
-
content = JSON.stringify(ctx.data);
|
|
103
|
-
ctx.set("content-type", "application/json");
|
|
112
|
+
var normalizeEmptyArraysForCsv = val => {
|
|
113
|
+
if (Array.isArray(val)) {
|
|
114
|
+
if (val.length === 0) return undefined;
|
|
115
|
+
return val.map(v => normalizeEmptyArraysForCsv(v));
|
|
104
116
|
}
|
|
105
117
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
118
|
+
if (val && typeof val === "object") {
|
|
119
|
+
var out = {};
|
|
120
|
+
|
|
121
|
+
for (var [k, v] of Object.entries(val)) {
|
|
122
|
+
var nv = normalizeEmptyArraysForCsv(v);
|
|
123
|
+
if (nv !== undefined) out[k] = nv;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return val;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
var safeJSONStringify = function safeJSONStringify(value, space) {
|
|
133
|
+
if (space === void 0) {
|
|
134
|
+
space = 2;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
var seen = new WeakSet();
|
|
138
|
+
|
|
139
|
+
var replacer = (_k, v) => {
|
|
140
|
+
if (typeof v === "bigint") return v.toString();
|
|
141
|
+
if (v instanceof Date) return v.toISOString();
|
|
142
|
+
|
|
143
|
+
if (v && typeof v === "object") {
|
|
144
|
+
if (seen.has(v)) return "[Circular]";
|
|
145
|
+
seen.add(v);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return v;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
return JSON.stringify(value, replacer, space);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
var download_type = (ctx.request.query.format || "csv").toLowerCase();
|
|
156
|
+
var include_id = ctx.request.query.include_id;
|
|
157
|
+
|
|
158
|
+
var original = _lodash.default.cloneDeep(ctx.data);
|
|
159
|
+
|
|
160
|
+
var trimmed = deepTrimStrings(original);
|
|
161
|
+
var cleaned = stripTopLevel(trimmed, {
|
|
162
|
+
removeId: !include_id || include_id === "false"
|
|
111
163
|
});
|
|
112
|
-
|
|
113
|
-
|
|
164
|
+
var filename, filePath;
|
|
165
|
+
|
|
166
|
+
if (download_type === "csv") {
|
|
167
|
+
var _ret = yield* function* () {
|
|
168
|
+
var {
|
|
169
|
+
flatten
|
|
170
|
+
} = yield Promise.resolve().then(() => _interopRequireWildcard(require("flat")));
|
|
171
|
+
var rows = Array.isArray(cleaned) ? cleaned : [cleaned];
|
|
172
|
+
var csvReady = rows.map(normalizeEmptyArraysForCsv);
|
|
173
|
+
var flatRows = csvReady.map(row => flatten(row, {
|
|
174
|
+
safe: false,
|
|
175
|
+
delimiter: "."
|
|
176
|
+
}));
|
|
177
|
+
var fieldSet = new Set();
|
|
178
|
+
|
|
179
|
+
for (var r of flatRows) {
|
|
180
|
+
Object.keys(r).forEach(k => fieldSet.add(k));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
var fields = Array.from(fieldSet).sort((a, b) => a.localeCompare(b, undefined, {
|
|
184
|
+
numeric: true,
|
|
185
|
+
sensitivity: "base"
|
|
186
|
+
}));
|
|
187
|
+
var csv = parse(flatRows, {
|
|
188
|
+
fields,
|
|
189
|
+
defaultValue: "",
|
|
190
|
+
excelStrings: true
|
|
191
|
+
});
|
|
192
|
+
makeDirectory("./storage/exports/csv");
|
|
193
|
+
filename = RestHelper.makeid(9) + ".csv";
|
|
194
|
+
filePath = path.resolve("./storage/exports/csv/" + filename);
|
|
195
|
+
yield fs.promises.writeFile(filePath, csv, "utf8");
|
|
196
|
+
ctx.set("Content-Disposition", "attachment; filename=\"" + filename + "\"");
|
|
197
|
+
ctx.type = "text/csv; charset=utf-8";
|
|
198
|
+
ctx.status = 200;
|
|
199
|
+
ctx.body = fs.createReadStream(filePath);
|
|
200
|
+
return {
|
|
201
|
+
v: void 0
|
|
202
|
+
};
|
|
203
|
+
}();
|
|
204
|
+
|
|
205
|
+
if (typeof _ret === "object") return _ret.v;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
var jsonText = safeJSONStringify(cleaned, 2);
|
|
209
|
+
makeDirectory("./storage/exports/json");
|
|
210
|
+
filename = RestHelper.makeid(9) + ".json";
|
|
211
|
+
filePath = path.resolve("./storage/exports/json/" + filename);
|
|
212
|
+
yield fs.promises.writeFile(filePath, jsonText, "utf8");
|
|
213
|
+
ctx.set("Content-Disposition", "attachment; filename=\"" + filename + "\"");
|
|
214
|
+
ctx.type = "application/json; charset=utf-8";
|
|
114
215
|
ctx.status = 200;
|
|
115
|
-
ctx.body = fs.createReadStream(
|
|
216
|
+
ctx.body = fs.createReadStream(filePath);
|
|
116
217
|
} catch (e) {
|
|
117
|
-
console.
|
|
218
|
+
console.error(e.stack);
|
|
118
219
|
ctx.status = 400;
|
|
119
220
|
ctx.body = RestHelper.prepare_response(RestHelper.FAILURE, e);
|
|
120
221
|
}
|
package/package.json
CHANGED
package/src/models/SpiceModel.js
CHANGED
|
@@ -825,66 +825,85 @@ export default class SpiceModel {
|
|
|
825
825
|
}
|
|
826
826
|
|
|
827
827
|
|
|
828
|
-
prepColumns(columns, protectedAliases = [], arrayAliases = []) {
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
828
|
+
prepColumns(columns, protectedAliases = [], arrayAliases = []) {
|
|
829
|
+
if (!columns || columns === "") return columns;
|
|
830
|
+
|
|
831
|
+
const q = (s = "") => {
|
|
832
|
+
const t = (s || "").trim();
|
|
833
|
+
if (t.startsWith("`") && t.endsWith("`")) return t;
|
|
834
|
+
return "`" + t.replace(/`/g, "``") + "`";
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
const protectedSet = new Set(protectedAliases);
|
|
838
|
+
const arraySet = new Set(arrayAliases);
|
|
839
|
+
|
|
840
|
+
const tokens = columns.split(",");
|
|
841
|
+
|
|
842
|
+
const out = tokens.map((raw) => {
|
|
843
|
+
let col = (raw || "").trim();
|
|
844
|
+
if (col === "" || col === "meta().id") return undefined;
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
if (/^\s*ARRAY\s+/i.test(col) || /\w+\s*\(/.test(col)) return col;
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
const m = col.match(/^\s*`?(\w+)`?\.`?(\w+)`?(?:\s+AS\s+`?([\w]+)`?)?\s*$/i);
|
|
851
|
+
if (m) {
|
|
852
|
+
const alias = m[1];
|
|
853
|
+
const field = m[2];
|
|
854
|
+
const explicitAs = m[3];
|
|
855
|
+
|
|
856
|
+
if (arraySet.has(alias)) {
|
|
857
|
+
let proj = this.buildArrayProjection(alias, field);
|
|
858
|
+
if (explicitAs && explicitAs !== `${alias}_${field}`) {
|
|
859
|
+
proj = proj.replace(/AS\s+`[^`]+`$/i, `AS ${q(explicitAs)}`);
|
|
860
|
+
}
|
|
861
|
+
return proj;
|
|
853
862
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
863
|
+
|
|
864
|
+
if (protectedSet.has(alias)) {
|
|
865
|
+
const aliased = `${q(alias)}.${field.startsWith("`") ? field : q(field)}`;
|
|
866
|
+
return explicitAs ? `${aliased} AS ${q(explicitAs)}` : aliased;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
const qualified = `${q(this.type)}.${q(alias)}.${q(field)}`;
|
|
871
|
+
return explicitAs ? `${qualified} AS ${q(explicitAs)}` : qualified;
|
|
860
872
|
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
if (!
|
|
872
|
-
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
const looksProtected = [...protectedSet].some(
|
|
876
|
+
(a) =>
|
|
877
|
+
col === a ||
|
|
878
|
+
col === q(a) ||
|
|
879
|
+
col.startsWith(`${a}.`) ||
|
|
880
|
+
col.startsWith(`${q(a)}.`)
|
|
881
|
+
);
|
|
882
|
+
|
|
883
|
+
if (!looksProtected) {
|
|
884
|
+
|
|
885
|
+
if (!col.includes(".")) {
|
|
886
|
+
const fieldQuoted = col.startsWith("`") ? col : q(col);
|
|
887
|
+
return `${q(this.type)}.${fieldQuoted}`;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const parts = col.split(".");
|
|
891
|
+
const safeParts = parts.map((p) => {
|
|
892
|
+
const t = p.trim();
|
|
893
|
+
if (t === "" || /\w+\s*\(/.test(t)) return t;
|
|
894
|
+
return t.startsWith("`") ? t : q(t);
|
|
895
|
+
});
|
|
896
|
+
return `${q(this.type)}.${safeParts.join(".")}`;
|
|
873
897
|
}
|
|
874
|
-
|
|
875
|
-
|
|
898
|
+
|
|
899
|
+
if (!col.includes(".") && !col.startsWith("`")) {
|
|
900
|
+
return q(col);
|
|
876
901
|
}
|
|
877
902
|
return col;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
}
|
|
883
|
-
return col;
|
|
884
|
-
});
|
|
885
|
-
|
|
886
|
-
return _.join(_.compact(out), ",");
|
|
887
|
-
}
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
return _.join(_.compact(out), ",");
|
|
906
|
+
}
|
|
888
907
|
|
|
889
908
|
|
|
890
909
|
filterResultsByColumns(data, columns) {
|
|
@@ -41,57 +41,112 @@ export default class RestHelper {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
static async send_download(ctx, next) {
|
|
44
|
-
|
|
45
|
-
if (!fs.existsSync(dir)) {
|
|
46
|
-
|
|
44
|
+
const makeDirectory = (dir) => {
|
|
45
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const deepTrimStrings = (val) => {
|
|
49
|
+
if (Array.isArray(val)) return val.map(deepTrimStrings);
|
|
50
|
+
if (val && typeof val === "object") {
|
|
51
|
+
const out = {};
|
|
52
|
+
for (const [k, v] of Object.entries(val)) out[k] = deepTrimStrings(v);
|
|
53
|
+
return out;
|
|
47
54
|
}
|
|
48
|
-
|
|
55
|
+
return typeof val === "string" ? val.trim() : val;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const stripTopLevel = (obj, { removeId }) => {
|
|
59
|
+
if (Array.isArray(obj)) return obj.map((i) => stripTopLevel(i, { removeId }));
|
|
60
|
+
if (obj && typeof obj === "object") {
|
|
61
|
+
const copy = { ...obj };
|
|
62
|
+
if (removeId) delete copy.id;
|
|
63
|
+
delete copy._permissions_;
|
|
64
|
+
return copy;
|
|
65
|
+
}
|
|
66
|
+
return obj;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const normalizeEmptyArraysForCsv = (val) => {
|
|
70
|
+
if (Array.isArray(val)) {
|
|
71
|
+
if (val.length === 0) return undefined;
|
|
72
|
+
return val.map((v) => normalizeEmptyArraysForCsv(v));
|
|
73
|
+
}
|
|
74
|
+
if (val && typeof val === "object") {
|
|
75
|
+
const out = {};
|
|
76
|
+
for (const [k, v] of Object.entries(val)) {
|
|
77
|
+
const nv = normalizeEmptyArraysForCsv(v);
|
|
78
|
+
if (nv !== undefined) out[k] = nv;
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
return val;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const safeJSONStringify = (value, space = 2) => {
|
|
86
|
+
const seen = new WeakSet();
|
|
87
|
+
const replacer = (_k, v) => {
|
|
88
|
+
if (typeof v === "bigint") return v.toString();
|
|
89
|
+
if (v instanceof Date) return v.toISOString();
|
|
90
|
+
if (v && typeof v === "object") {
|
|
91
|
+
if (seen.has(v)) return "[Circular]";
|
|
92
|
+
seen.add(v);
|
|
93
|
+
}
|
|
94
|
+
return v;
|
|
95
|
+
};
|
|
96
|
+
return JSON.stringify(value, replacer, space);
|
|
97
|
+
};
|
|
98
|
+
|
|
49
99
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
100
|
+
const download_type = (ctx.request.query.format || "csv").toLowerCase();
|
|
101
|
+
const include_id = ctx.request.query.include_id;
|
|
102
|
+
|
|
103
|
+
const original = _.cloneDeep(ctx.data);
|
|
104
|
+
const trimmed = deepTrimStrings(original);
|
|
105
|
+
const cleaned = stripTopLevel(trimmed, { removeId: !include_id || include_id === "false" });
|
|
106
|
+
|
|
107
|
+
let filename, filePath;
|
|
108
|
+
|
|
109
|
+
if (download_type === "csv") {
|
|
54
110
|
const { flatten } = await import("flat");
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
_.keys(_.first(items)),
|
|
65
|
-
_.keys(_.last(items)),
|
|
66
|
-
_.keys(_.nth(items.length / 2))
|
|
111
|
+
const rows = Array.isArray(cleaned) ? cleaned : [cleaned];
|
|
112
|
+
|
|
113
|
+
const csvReady = rows.map(normalizeEmptyArraysForCsv);
|
|
114
|
+
const flatRows = csvReady.map((row) => flatten(row, { safe: false, delimiter: "." }));
|
|
115
|
+
|
|
116
|
+
const fieldSet = new Set();
|
|
117
|
+
for (const r of flatRows) Object.keys(r).forEach((k) => fieldSet.add(k));
|
|
118
|
+
const fields = Array.from(fieldSet).sort((a, b) =>
|
|
119
|
+
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" })
|
|
67
120
|
);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
121
|
+
|
|
122
|
+
const csv = parse(flatRows, { fields, defaultValue: "", excelStrings: true });
|
|
123
|
+
|
|
124
|
+
makeDirectory(`./storage/exports/csv`);
|
|
125
|
+
filename = `${RestHelper.makeid(9)}.csv`;
|
|
126
|
+
filePath = path.resolve(`./storage/exports/csv/${filename}`);
|
|
127
|
+
await fs.promises.writeFile(filePath, csv, "utf8");
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
ctx.set("Content-Disposition", `attachment; filename="${filename}"`);
|
|
131
|
+
ctx.type = "text/csv; charset=utf-8";
|
|
132
|
+
ctx.status = 200;
|
|
133
|
+
ctx.body = fs.createReadStream(filePath);
|
|
134
|
+
return;
|
|
76
135
|
}
|
|
77
136
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
if (err) throw err;
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
ctx.set("content-disposition", "attachment");
|
|
90
|
-
ctx.response.attachment(file);
|
|
137
|
+
const jsonText = safeJSONStringify(cleaned, 2);
|
|
138
|
+
|
|
139
|
+
makeDirectory(`./storage/exports/json`);
|
|
140
|
+
filename = `${RestHelper.makeid(9)}.json`;
|
|
141
|
+
filePath = path.resolve(`./storage/exports/json/${filename}`);
|
|
142
|
+
await fs.promises.writeFile(filePath, jsonText, "utf8");
|
|
143
|
+
ctx.set("Content-Disposition", `attachment; filename="${filename}"`);
|
|
144
|
+
ctx.type = "application/json; charset=utf-8";
|
|
91
145
|
ctx.status = 200;
|
|
92
|
-
ctx.body = fs.createReadStream(
|
|
146
|
+
ctx.body = fs.createReadStream(filePath);
|
|
147
|
+
|
|
93
148
|
} catch (e) {
|
|
94
|
-
console.
|
|
149
|
+
console.error(e.stack);
|
|
95
150
|
ctx.status = 400;
|
|
96
151
|
ctx.body = RestHelper.prepare_response(RestHelper.FAILURE, e);
|
|
97
152
|
}
|
|
@@ -203,3 +258,4 @@ export default class RestHelper {
|
|
|
203
258
|
return false;
|
|
204
259
|
}
|
|
205
260
|
}
|
|
261
|
+
|