@jd-data-limited/easy-fm 0.0.1
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/.idea/easy-fm.iml +8 -0
- package/.idea/jsLibraryMappings.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/LICENSE +21 -0
- package/README.md +64 -0
- package/decs.d.ts +4 -0
- package/fmErrors.json +1010 -0
- package/img.png +0 -0
- package/index.js +455 -0
- package/index.ts +533 -0
- package/package.json +34 -0
- package/tsconfig.json +8 -0
package/img.png
ADDED
|
Binary file
|
package/index.js
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.FMError = void 0;
|
|
13
|
+
// const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
|
|
14
|
+
const node_fetch_1 = require("node-fetch");
|
|
15
|
+
const events_1 = require("events");
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
const fs_1 = require("fs");
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
const path_1 = require("path");
|
|
20
|
+
// @ts-ignore
|
|
21
|
+
const url_1 = require("url");
|
|
22
|
+
const btoa = require("btoa");
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
const errs = JSON.parse(fs_1.default.readFileSync(path_1.default.join(path_1.default.dirname((0, url_1.fileURLToPath)(import.meta.url)), 'fmErrors.json')).toString());
|
|
25
|
+
class FileMakerConnection {
|
|
26
|
+
constructor() {
|
|
27
|
+
}
|
|
28
|
+
get endpoint() {
|
|
29
|
+
return `https://${this.hostname}/fmi/data/v2/databases/${encodeURI(this.database)}`;
|
|
30
|
+
}
|
|
31
|
+
login(hostname, database, username, password, rejectUnauthorized = true) {
|
|
32
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
if (this.token)
|
|
34
|
+
throw new Error("Already logged in. Run logout() first");
|
|
35
|
+
this.username = username;
|
|
36
|
+
this.password = password;
|
|
37
|
+
this.hostname = hostname;
|
|
38
|
+
this.database = database;
|
|
39
|
+
this.rejectUnauthroized = rejectUnauthorized;
|
|
40
|
+
return yield this.createSession();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
logout() {
|
|
44
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
if (!this.token)
|
|
46
|
+
reject(new Error("Not logged in"));
|
|
47
|
+
let _fetch = yield (0, node_fetch_1.default)(`${this.endpoint}/sessions/${this.token}`, {
|
|
48
|
+
method: "DELETE",
|
|
49
|
+
headers: {
|
|
50
|
+
"content-type": "application/json"
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
let data = yield _fetch.json();
|
|
54
|
+
// console.log(data)
|
|
55
|
+
this.token = null;
|
|
56
|
+
this.database = null;
|
|
57
|
+
this.hostname = null;
|
|
58
|
+
resolve();
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
importSession(hostname, database, token, rejectUnauthorized = true) {
|
|
62
|
+
this.token = token;
|
|
63
|
+
this.database = database;
|
|
64
|
+
this.hostname = hostname;
|
|
65
|
+
this.rejectUnauthroized = rejectUnauthorized;
|
|
66
|
+
}
|
|
67
|
+
createSession() {
|
|
68
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
(0, node_fetch_1.default)(`${this.endpoint}/sessions`, {
|
|
70
|
+
hostname: this.hostname,
|
|
71
|
+
port: 443,
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: {
|
|
74
|
+
"Content-Type": "application/json",
|
|
75
|
+
"Authorization": "Basic " + btoa(this.username + ":" + this.password)
|
|
76
|
+
}
|
|
77
|
+
}).then(res => {
|
|
78
|
+
console.log(res);
|
|
79
|
+
return res.json();
|
|
80
|
+
})
|
|
81
|
+
.then(res => {
|
|
82
|
+
let _res = res;
|
|
83
|
+
if (_res.messages[0].code === "0") {
|
|
84
|
+
this.token = _res.response.token;
|
|
85
|
+
console.log(_res);
|
|
86
|
+
resolve(this.token);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
reject(new FMError(_res.messages[0].code, _res.status, res));
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
.catch(e => {
|
|
93
|
+
reject(e);
|
|
94
|
+
});
|
|
95
|
+
}));
|
|
96
|
+
}
|
|
97
|
+
getLayout(name) {
|
|
98
|
+
return new layout(this, name);
|
|
99
|
+
}
|
|
100
|
+
apiRequest(url, options) {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
if (!options.headers)
|
|
103
|
+
options.headers = {};
|
|
104
|
+
options.headers["content-type"] = options.headers["content-type"] ? options.headers["content-type"] : "application/json";
|
|
105
|
+
options.headers["authorization"] = "Bearer " + this.token;
|
|
106
|
+
options.rejectUnauthorized = this.rejectUnauthroized;
|
|
107
|
+
let _fetch = yield (0, node_fetch_1.default)(url, options);
|
|
108
|
+
let data = yield _fetch.json();
|
|
109
|
+
return data;
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
setGlobals(globalFields) {
|
|
113
|
+
// console.log({globalFields})
|
|
114
|
+
return new Promise((resolve, reject) => {
|
|
115
|
+
this.apiRequest(`${this.endpoint}/globals`, {
|
|
116
|
+
method: "PATCH",
|
|
117
|
+
body: JSON.stringify({ globalFields })
|
|
118
|
+
}).then(res => {
|
|
119
|
+
// console.log(res)
|
|
120
|
+
if (res.messages[0].code === "0") {
|
|
121
|
+
resolve();
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
reject(new FMError(res.messages[0].code, res.status, res));
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
.catch(e => {
|
|
128
|
+
reject(e);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
script(name, parameter) {
|
|
133
|
+
return { name, parameter };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
exports.default = FileMakerConnection;
|
|
137
|
+
class layout {
|
|
138
|
+
constructor(conn, name) {
|
|
139
|
+
this.conn = conn;
|
|
140
|
+
this.name = name;
|
|
141
|
+
}
|
|
142
|
+
get endpoint() {
|
|
143
|
+
return `${this.conn.endpoint}/layouts/${this.name}`;
|
|
144
|
+
}
|
|
145
|
+
getScript(script) {
|
|
146
|
+
return new script(this, script);
|
|
147
|
+
}
|
|
148
|
+
createRecord(body = {}) {
|
|
149
|
+
// console.log(fieldData)
|
|
150
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
this.conn.apiRequest(`${this.endpoint}/records`, {
|
|
152
|
+
port: 443,
|
|
153
|
+
method: "POST",
|
|
154
|
+
body: JSON.stringify(body)
|
|
155
|
+
})
|
|
156
|
+
.then(res => {
|
|
157
|
+
if (res.messages[0].code === "0") {
|
|
158
|
+
resolve(new record(this, res.response.recordId, res.response.modId));
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
reject(new FMError(res.messages[0].code, res.status, res));
|
|
162
|
+
}
|
|
163
|
+
})
|
|
164
|
+
.catch(e => {
|
|
165
|
+
reject(e);
|
|
166
|
+
});
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
getRecord(recordId) {
|
|
170
|
+
return new record(this, recordId);
|
|
171
|
+
}
|
|
172
|
+
newFind() {
|
|
173
|
+
return new find(this);
|
|
174
|
+
}
|
|
175
|
+
runScript(script) {
|
|
176
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
177
|
+
let url = `${this.endpoint}/script/${encodeURI(script.name)}`;
|
|
178
|
+
if (script.parameter)
|
|
179
|
+
url += "?" + encodeURI(script.parameter);
|
|
180
|
+
this.conn.apiRequest(url, {
|
|
181
|
+
port: 443,
|
|
182
|
+
method: "GET"
|
|
183
|
+
})
|
|
184
|
+
.then(res => {
|
|
185
|
+
if (res.messages[0].code === "0") {
|
|
186
|
+
resolve(res.response);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
// console.log(res)
|
|
190
|
+
reject(new FMError(res.messages[0].code, res.status, res));
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
.catch(e => {
|
|
194
|
+
reject(e);
|
|
195
|
+
});
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
class record extends events_1.EventEmitter {
|
|
200
|
+
constructor(layout, recordId, modId = recordId, fieldData = {}, portalData = null) {
|
|
201
|
+
super();
|
|
202
|
+
this.layout = layout;
|
|
203
|
+
this.recordId = recordId;
|
|
204
|
+
this.modId = modId;
|
|
205
|
+
this.fields = this.processFieldData(fieldData);
|
|
206
|
+
this.portals = [];
|
|
207
|
+
if (portalData) {
|
|
208
|
+
this.processPortalData(portalData);
|
|
209
|
+
}
|
|
210
|
+
this.on("saved", () => {
|
|
211
|
+
for (let field of this.fields)
|
|
212
|
+
field.edited = false;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
get endpoint() {
|
|
216
|
+
return `${this.layout.endpoint}/records/${this.recordId}`;
|
|
217
|
+
}
|
|
218
|
+
get edited() {
|
|
219
|
+
return !!this.fields.find(i => i.edited);
|
|
220
|
+
}
|
|
221
|
+
processPortalData(portalData) {
|
|
222
|
+
for (let item of Object.keys(portalData)) {
|
|
223
|
+
this.portals.push(new portal(this, item, portalData[item].map(item => {
|
|
224
|
+
let fieldData = item;
|
|
225
|
+
delete fieldData.recordId;
|
|
226
|
+
delete fieldData.modId;
|
|
227
|
+
return new portalItem(this, item.recordId, item.modId, fieldData);
|
|
228
|
+
})));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
processFieldData(fieldData) {
|
|
232
|
+
return Object.keys(fieldData).map(item => {
|
|
233
|
+
return new field(this, item, fieldData[item]);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
get() {
|
|
237
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
238
|
+
this.layout.conn.apiRequest(this.endpoint, {
|
|
239
|
+
port: 443,
|
|
240
|
+
method: "GET"
|
|
241
|
+
})
|
|
242
|
+
.then(res => {
|
|
243
|
+
if (res.messages[0].code === "0") {
|
|
244
|
+
// console.log(res, res.response.data)
|
|
245
|
+
this.modId = res.response.data[0].modId;
|
|
246
|
+
this.fields = this.processFieldData(res.response.data[0].fieldData);
|
|
247
|
+
this.portalData = [];
|
|
248
|
+
if (res.response.data[0].portalData)
|
|
249
|
+
this.processPortalData(res.response.data[0].portalData);
|
|
250
|
+
resolve(this);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
reject(new FMError(res.messages[0].code, res.status, res));
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
.catch(e => {
|
|
257
|
+
reject(e);
|
|
258
|
+
});
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
save(extraBody = {}) {
|
|
262
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
263
|
+
let data = this.toObject();
|
|
264
|
+
delete data.recordId;
|
|
265
|
+
delete data.modId;
|
|
266
|
+
for (let item of Object.keys(extraBody))
|
|
267
|
+
data[item] = extraBody[item];
|
|
268
|
+
this.layout.conn.apiRequest(this.endpoint, {
|
|
269
|
+
port: 443,
|
|
270
|
+
method: "PATCH",
|
|
271
|
+
body: JSON.stringify(data)
|
|
272
|
+
})
|
|
273
|
+
.then(res => {
|
|
274
|
+
if (res.messages[0].code === "0") {
|
|
275
|
+
this.modId = res.response.modId;
|
|
276
|
+
this.emit("saved");
|
|
277
|
+
resolve(this);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
reject(new FMError(res.messages[0].code, res.status, res));
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
.catch(e => {
|
|
284
|
+
reject(e);
|
|
285
|
+
});
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
getField(field) {
|
|
289
|
+
return this.fields.find(_field => _field.id === field);
|
|
290
|
+
}
|
|
291
|
+
getPortal(portal) {
|
|
292
|
+
return this.portals.find(p => p.name === portal);
|
|
293
|
+
}
|
|
294
|
+
toObject(filter = (a) => a.edited, portalFilter = (a) => a.records.find(record => record.edited), portalRowFilter = (a) => a.edited, portalFieldFilter = (a) => a.edited) {
|
|
295
|
+
let fields_processed = {};
|
|
296
|
+
for (let field of this.fields.filter(field => filter(field))) {
|
|
297
|
+
fields_processed[field.id] = field.value;
|
|
298
|
+
}
|
|
299
|
+
let obj = {
|
|
300
|
+
"recordId": this.recordId,
|
|
301
|
+
"modId": this.modId,
|
|
302
|
+
"fieldData": fields_processed
|
|
303
|
+
};
|
|
304
|
+
// Check if there's been any edited portal information
|
|
305
|
+
let portals = this.portals.filter(a => portalFilter(a));
|
|
306
|
+
if (portals) {
|
|
307
|
+
obj["portalData"] = {};
|
|
308
|
+
for (let portal of portals) {
|
|
309
|
+
obj["portalData"][portal.name] = portal.records.filter(a => portalRowFilter(a)).map(record => {
|
|
310
|
+
return record.toObject(portalFieldFilter);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return obj;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
class field {
|
|
318
|
+
constructor(record, id, contents) {
|
|
319
|
+
this.record = record;
|
|
320
|
+
this.id = id;
|
|
321
|
+
this.value = contents;
|
|
322
|
+
this.edited = false;
|
|
323
|
+
}
|
|
324
|
+
set(content) {
|
|
325
|
+
this.value = content;
|
|
326
|
+
this.edited = true;
|
|
327
|
+
}
|
|
328
|
+
containerUpload(buffer, filename, mime) {
|
|
329
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
330
|
+
let form = new node_fetch_1.FormData();
|
|
331
|
+
form.append("upload", new node_fetch_1.File([buffer], filename, { type: mime }));
|
|
332
|
+
let _fetch = yield (0, node_fetch_1.default)(`${this.record.endpoint}/containers/${this.id}/1`, {
|
|
333
|
+
method: "POST",
|
|
334
|
+
headers: { "Authorization": "Bearer " + this.record.layout.conn.token },
|
|
335
|
+
body: form
|
|
336
|
+
}).then(res => res.json())
|
|
337
|
+
.then(data => {
|
|
338
|
+
let _res = data;
|
|
339
|
+
if (_res.messages[0].code === "0") {
|
|
340
|
+
resolve();
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
reject(new FMError(_res.messages[0].code, _res.status, data));
|
|
344
|
+
}
|
|
345
|
+
})
|
|
346
|
+
.catch(e => { reject(e); });
|
|
347
|
+
}));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
class portal {
|
|
351
|
+
constructor(record, name, records = []) {
|
|
352
|
+
this.record = record;
|
|
353
|
+
this.name = name;
|
|
354
|
+
this.records = records;
|
|
355
|
+
for (let item of this.records)
|
|
356
|
+
item.attachPortal(this);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
class portalItem extends record {
|
|
360
|
+
constructor(record, recordId, modId = recordId, fieldData = {}) {
|
|
361
|
+
super(record.layout, recordId, modId, fieldData);
|
|
362
|
+
record.on("saved", () => {
|
|
363
|
+
this.emit("saved");
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
attachPortal(portal) {
|
|
367
|
+
this.portal = portal;
|
|
368
|
+
}
|
|
369
|
+
save(extraBody = {}) {
|
|
370
|
+
return this.portal.record.save(extraBody);
|
|
371
|
+
}
|
|
372
|
+
toObject(fieldFilter) {
|
|
373
|
+
super.toObject();
|
|
374
|
+
let res = {
|
|
375
|
+
recordId: this.recordId, modId: this.modId
|
|
376
|
+
};
|
|
377
|
+
for (let field of this.fields.filter(a => fieldFilter(a)))
|
|
378
|
+
res[field.id] = field.value;
|
|
379
|
+
// console.log(res)
|
|
380
|
+
return res;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
class find {
|
|
384
|
+
constructor(layout) {
|
|
385
|
+
this.layout = layout;
|
|
386
|
+
this.queries = [];
|
|
387
|
+
this.scripts = {
|
|
388
|
+
"script": null,
|
|
389
|
+
"script.prerequeset": null,
|
|
390
|
+
"script.presort": null // Runs before the sort
|
|
391
|
+
};
|
|
392
|
+
this.sort = [];
|
|
393
|
+
}
|
|
394
|
+
toObject() {
|
|
395
|
+
let out = { query: this.queries, sort: undefined };
|
|
396
|
+
if (this.sort.length !== 0)
|
|
397
|
+
out.sort = this.sort;
|
|
398
|
+
for (let item of Object.keys(this.scripts)) {
|
|
399
|
+
if (this.scripts[item]) {
|
|
400
|
+
out[item] = this.scripts[item].name;
|
|
401
|
+
if (this.scripts[item].parameter)
|
|
402
|
+
out[item + ".param"] = this.scripts[item].parameter;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return out;
|
|
406
|
+
}
|
|
407
|
+
addRequests(...requests) {
|
|
408
|
+
for (let item of requests)
|
|
409
|
+
this.queries.push(item);
|
|
410
|
+
return this;
|
|
411
|
+
}
|
|
412
|
+
addSort(fieldName, sortOrder) {
|
|
413
|
+
this.sort.push({ fieldName, sortOrder });
|
|
414
|
+
return this;
|
|
415
|
+
}
|
|
416
|
+
find() {
|
|
417
|
+
return new Promise((resolve, reject) => {
|
|
418
|
+
// console.log(this.#toObject())
|
|
419
|
+
this.layout.conn.apiRequest(`${this.layout.endpoint}/_find`, {
|
|
420
|
+
port: 443,
|
|
421
|
+
method: "POST",
|
|
422
|
+
body: JSON.stringify(this.toObject())
|
|
423
|
+
}).then(res => {
|
|
424
|
+
// // console.log(res)
|
|
425
|
+
if (res.messages[0].code === "0") {
|
|
426
|
+
// console.log("RESOLVING")
|
|
427
|
+
let data = res.response.data.map(item => {
|
|
428
|
+
return new record(this.layout, item.recordId, item.modId, item.fieldData, item.portalData);
|
|
429
|
+
});
|
|
430
|
+
resolve(data);
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
reject(new FMError(res.messages[0].code, res.status, res));
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
.catch(e => {
|
|
437
|
+
reject(e);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
class FMError extends Error {
|
|
443
|
+
constructor(code, httpStatus, res) {
|
|
444
|
+
super(code);
|
|
445
|
+
this.httpStatus = httpStatus;
|
|
446
|
+
this.res = res;
|
|
447
|
+
this.code = typeof code === "string" ? parseInt(code) : code;
|
|
448
|
+
this.message = errs.find(err => err.e === this.code) || "Unknown error";
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
exports.FMError = FMError;
|
|
452
|
+
//
|
|
453
|
+
// module.exports = {
|
|
454
|
+
// default: {FileMakerConnection}
|
|
455
|
+
// }
|