@jd-data-limited/easy-fm 1.0.1-alpha1 → 1.0.2-alpha2
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/.expo/README.md +15 -0
- package/.expo/settings.json +8 -0
- package/index.js +107 -9
- package/index.ts +174 -77
- package/package.json +1 -1
package/.expo/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
> Why do I have a folder named ".expo" in my project?
|
|
2
|
+
|
|
3
|
+
The ".expo" folder is created when an Expo project is started using "expo start" command.
|
|
4
|
+
|
|
5
|
+
> What do the files contain?
|
|
6
|
+
|
|
7
|
+
- "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds.
|
|
8
|
+
- "packager-info.json": contains port numbers and process PIDs that are used to serve the application to the mobile device/simulator.
|
|
9
|
+
- "settings.json": contains the server configuration that is used to serve the application manifest.
|
|
10
|
+
|
|
11
|
+
> Should I commit the ".expo" folder?
|
|
12
|
+
|
|
13
|
+
No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine.
|
|
14
|
+
|
|
15
|
+
Upon project creation, the ".expo" folder is already added to your ".gitignore" file.
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c) 2022. See LICENSE file for more information
|
|
2
|
+
* Copyright (c) 2022-2023. See LICENSE file for more information
|
|
3
3
|
*/
|
|
4
4
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
5
5
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
@@ -102,7 +102,11 @@ export default class FileMakerConnection extends database {
|
|
|
102
102
|
super(conn.database);
|
|
103
103
|
this.host = this;
|
|
104
104
|
this.hostname = conn.hostname;
|
|
105
|
-
this.externalSources = externalDataSources.map(i => {
|
|
105
|
+
this.externalSources = externalDataSources.map(i => {
|
|
106
|
+
let e = new database(i, this);
|
|
107
|
+
e.external = true;
|
|
108
|
+
return e;
|
|
109
|
+
});
|
|
106
110
|
this.rejectUnauthroized = rejectUnauthorized;
|
|
107
111
|
}
|
|
108
112
|
logout() {
|
|
@@ -459,7 +463,9 @@ class field {
|
|
|
459
463
|
reject(new FMError(_res.messages[0].code, _res.status, data));
|
|
460
464
|
}
|
|
461
465
|
})
|
|
462
|
-
.catch(e => {
|
|
466
|
+
.catch(e => {
|
|
467
|
+
reject(e);
|
|
468
|
+
});
|
|
463
469
|
}));
|
|
464
470
|
}
|
|
465
471
|
download() {
|
|
@@ -503,10 +509,12 @@ class portalItem extends record {
|
|
|
503
509
|
return res;
|
|
504
510
|
}
|
|
505
511
|
}
|
|
506
|
-
class
|
|
512
|
+
class recordGetOperation {
|
|
507
513
|
constructor(layout) {
|
|
514
|
+
this.limit = 100;
|
|
515
|
+
this.limitPortals = [];
|
|
516
|
+
this.offset = 0;
|
|
508
517
|
this.layout = layout;
|
|
509
|
-
this.queries = [];
|
|
510
518
|
this.scripts = {
|
|
511
519
|
"script": null,
|
|
512
520
|
"script.prerequeset": null,
|
|
@@ -514,6 +522,86 @@ class find {
|
|
|
514
522
|
};
|
|
515
523
|
this.sort = [];
|
|
516
524
|
}
|
|
525
|
+
/*
|
|
526
|
+
addToPortalLimit() will adjust the results of the get request so that if a layout has multiple portals in it,
|
|
527
|
+
only data from the specified ones will be read. This may help reduce load on your FileMaker API
|
|
528
|
+
*/
|
|
529
|
+
addToPortalLimit(portal, offset = 0, limit = 100) {
|
|
530
|
+
if (offset < 0)
|
|
531
|
+
throw "Portal offset cannot be less than 0";
|
|
532
|
+
this.limitPortals.push({ portal, offset, limit });
|
|
533
|
+
}
|
|
534
|
+
setLimit(limit) {
|
|
535
|
+
if (limit < 1)
|
|
536
|
+
throw "Record limit too low";
|
|
537
|
+
this.limit = limit;
|
|
538
|
+
}
|
|
539
|
+
addSort(fieldName, sortOrder) {
|
|
540
|
+
this.sort.push({ fieldName, sortOrder });
|
|
541
|
+
return this;
|
|
542
|
+
}
|
|
543
|
+
setOffset(offset) {
|
|
544
|
+
if (offset < 0)
|
|
545
|
+
throw "Record offset too low";
|
|
546
|
+
this.limit = offset;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
class recordGetRange extends recordGetOperation {
|
|
550
|
+
constructor(layout) {
|
|
551
|
+
super(layout);
|
|
552
|
+
}
|
|
553
|
+
generateQueryParams() {
|
|
554
|
+
let params = [];
|
|
555
|
+
if (this.limit !== 100)
|
|
556
|
+
params.push("_limit=" + this.limit);
|
|
557
|
+
if (this.offset !== 0)
|
|
558
|
+
params.push("_offset=" + this.offset);
|
|
559
|
+
if (this.sort.length > 0)
|
|
560
|
+
params.push("_sort=" + encodeURI(JSON.stringify(this.sort)));
|
|
561
|
+
if (this.limitPortals.length > 0) {
|
|
562
|
+
params.push("portal=" + encodeURI(JSON.stringify(this.limitPortals.map(p => p.portal.name))));
|
|
563
|
+
for (let item of this.limitPortals) {
|
|
564
|
+
if (item.offset !== 0)
|
|
565
|
+
params.push("_offset." + item.portal.name.replace(/[^0-9A-z]/g, "") + "=" + item.offset);
|
|
566
|
+
if (item.limit !== 100)
|
|
567
|
+
params.push("_limit." + item.portal.name.replace(/[^0-9A-z]/g, "") + "=" + item.limit);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (params.length === 0)
|
|
571
|
+
return "";
|
|
572
|
+
return "?" + params.join("&");
|
|
573
|
+
}
|
|
574
|
+
run() {
|
|
575
|
+
return new Promise((resolve, reject) => {
|
|
576
|
+
// console.log(this.#toObject())
|
|
577
|
+
this.layout.database.apiRequest(`${this.layout.endpoint}/records${this.generateQueryParams()}`, {
|
|
578
|
+
method: "GET"
|
|
579
|
+
}).then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
580
|
+
// // console.log(res)
|
|
581
|
+
if (res.messages[0].code === "0") {
|
|
582
|
+
// console.log("RESOLVING")
|
|
583
|
+
if (!this.layout.metadata)
|
|
584
|
+
yield this.layout.getLayoutMeta();
|
|
585
|
+
let data = res.response.data.map(item => {
|
|
586
|
+
return new record(this.layout, item.recordId, item.modId, item.fieldData, item.portalData);
|
|
587
|
+
});
|
|
588
|
+
resolve(data);
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
reject(new FMError(res.messages[0].code, res.status, res));
|
|
592
|
+
}
|
|
593
|
+
}))
|
|
594
|
+
.catch(e => {
|
|
595
|
+
reject(e);
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
class find extends recordGetOperation {
|
|
601
|
+
constructor(layout) {
|
|
602
|
+
super(layout);
|
|
603
|
+
this.queries = [];
|
|
604
|
+
}
|
|
517
605
|
toObject() {
|
|
518
606
|
let out = { query: this.queries, sort: undefined };
|
|
519
607
|
if (this.sort.length !== 0)
|
|
@@ -525,6 +613,17 @@ class find {
|
|
|
525
613
|
out[item + ".param"] = this.scripts[item].parameter;
|
|
526
614
|
}
|
|
527
615
|
}
|
|
616
|
+
if (this.limit !== 100)
|
|
617
|
+
out["limit"] = this.limit;
|
|
618
|
+
if (this.offset !== 0)
|
|
619
|
+
out["offset"] = this.offset;
|
|
620
|
+
if (this.limitPortals.length > 0) {
|
|
621
|
+
out["portal"] = this.limitPortals.map(p => p.portal.name);
|
|
622
|
+
for (let item of this.limitPortals) {
|
|
623
|
+
out["offset." + item.portal.name.replace(/[^0-9A-z]/g, "")] = item.offset;
|
|
624
|
+
out["limit." + item.portal.name.replace(/[^0-9A-z]/g, "")] = item.limit;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
528
627
|
return out;
|
|
529
628
|
}
|
|
530
629
|
addRequests(...requests) {
|
|
@@ -532,11 +631,10 @@ class find {
|
|
|
532
631
|
this.queries.push(item);
|
|
533
632
|
return this;
|
|
534
633
|
}
|
|
535
|
-
addSort(fieldName, sortOrder) {
|
|
536
|
-
this.sort.push({ fieldName, sortOrder });
|
|
537
|
-
return this;
|
|
538
|
-
}
|
|
539
634
|
find() {
|
|
635
|
+
return this.run();
|
|
636
|
+
}
|
|
637
|
+
run() {
|
|
540
638
|
return new Promise((resolve, reject) => {
|
|
541
639
|
// console.log(this.#toObject())
|
|
542
640
|
this.layout.database.apiRequest(`${this.layout.endpoint}/_find`, {
|
package/index.ts
CHANGED
|
@@ -15,14 +15,15 @@ import fetch, {
|
|
|
15
15
|
Request,
|
|
16
16
|
Response
|
|
17
17
|
} from 'node-fetch';
|
|
18
|
-
import {
|
|
18
|
+
import {EventEmitter} from "events";
|
|
19
19
|
// @ts-ignore
|
|
20
20
|
import fs from "fs"
|
|
21
21
|
// @ts-ignore
|
|
22
22
|
import path from "path"
|
|
23
23
|
// @ts-ignore
|
|
24
|
-
import {
|
|
24
|
+
import {fileURLToPath} from "url"
|
|
25
25
|
import * as https from "https";
|
|
26
|
+
|
|
26
27
|
// import * as btoa from "btoa";
|
|
27
28
|
|
|
28
29
|
interface connectionOptions {
|
|
@@ -32,7 +33,7 @@ interface connectionOptions {
|
|
|
32
33
|
|
|
33
34
|
interface hostConnectionOptions {
|
|
34
35
|
hostname: string
|
|
35
|
-
|
|
36
|
+
databases: (loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris | loginOptionsToken)[]
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
interface databaseOptions {
|
|
@@ -66,6 +67,12 @@ interface loginOptionsClaris extends databaseOptions {
|
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
interface limitPortalsInterface {
|
|
71
|
+
portal: portal,
|
|
72
|
+
offset: number,
|
|
73
|
+
limit: number
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
// @ts-ignore
|
|
70
77
|
const errs = JSON.parse(fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'fmErrors.json')).toString())
|
|
71
78
|
|
|
@@ -89,11 +96,7 @@ class database {
|
|
|
89
96
|
|
|
90
97
|
public hostname: string;
|
|
91
98
|
|
|
92
|
-
constructor(
|
|
93
|
-
this.props = props
|
|
94
|
-
this.name = this.props.database
|
|
95
|
-
if (host) this.host = host
|
|
96
|
-
}
|
|
99
|
+
constructor() {}
|
|
97
100
|
|
|
98
101
|
get token() {
|
|
99
102
|
return this.host.token
|
|
@@ -139,47 +142,36 @@ class database {
|
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
get endpoint(): string {
|
|
142
|
-
return
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
case "token":
|
|
159
|
-
throw "Token logins cannot be used for connecting to external sources. Please either use no login method, or open a second connection."
|
|
160
|
-
case "oauth":
|
|
161
|
-
out.oauthRequestId = (<loginOptionsOAuth>this.props).oauth.requestId
|
|
162
|
-
out.oauthIdentifier = (<loginOptionsOAuth>this.props).oauth.requestIdentifier
|
|
163
|
-
|
|
164
|
-
case "claris":
|
|
165
|
-
throw "Claris logins cannot be used for connecting to external sources. Please either use no login method, or open a second connection."
|
|
145
|
+
return `${this.host.endpoint}/${encodeURI(this.name)}`
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
pairHost(db: FileMakerConnection, details: databaseOptions, returnConDetails = true) {
|
|
149
|
+
this.host = db
|
|
150
|
+
this.name = details.database
|
|
151
|
+
if (returnConDetails) {
|
|
152
|
+
if (details.method === "filemaker") {
|
|
153
|
+
return {
|
|
154
|
+
database: this.name,
|
|
155
|
+
username: (<loginOptionsFileMaker>details).username,
|
|
156
|
+
password: (<loginOptionsFileMaker>details).password
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
throw `Authentication method used for '${this.name}' is not supported for external data source connections`
|
|
160
|
+
}
|
|
166
161
|
}
|
|
167
|
-
return out
|
|
168
162
|
}
|
|
169
163
|
}
|
|
170
164
|
|
|
171
|
-
export default class FileMakerConnection
|
|
165
|
+
export default class FileMakerConnection {
|
|
172
166
|
private _token: any;
|
|
173
|
-
private
|
|
174
|
-
private password: any;
|
|
167
|
+
private hostname: string;
|
|
175
168
|
public readonly rejectUnauthroized: boolean;
|
|
176
|
-
|
|
169
|
+
private databases: database[]
|
|
170
|
+
private databaseConDetails: (loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris | loginOptionsToken)[];
|
|
177
171
|
|
|
178
|
-
constructor(conn: hostConnectionOptions,
|
|
179
|
-
super(conn.database);
|
|
180
|
-
this.host = this
|
|
172
|
+
constructor(conn: hostConnectionOptions, rejectUnauthorized = true) {
|
|
181
173
|
this.hostname = conn.hostname
|
|
182
|
-
this.
|
|
174
|
+
this.databaseConDetails = conn.databases
|
|
183
175
|
this.rejectUnauthroized = rejectUnauthorized
|
|
184
176
|
}
|
|
185
177
|
|
|
@@ -196,7 +188,6 @@ export default class FileMakerConnection extends database{
|
|
|
196
188
|
let data = await _fetch.json()
|
|
197
189
|
// console.log(data)
|
|
198
190
|
this._token = null
|
|
199
|
-
this.name = null
|
|
200
191
|
this.hostname = null
|
|
201
192
|
resolve()
|
|
202
193
|
})
|
|
@@ -205,19 +196,26 @@ export default class FileMakerConnection extends database{
|
|
|
205
196
|
login() {
|
|
206
197
|
return new Promise<string>((resolve, reject) => {
|
|
207
198
|
if (this.token) throw new Error("Already logged in. Run logout() first")
|
|
199
|
+
this.databases = [new database()]
|
|
200
|
+
this.databases[0].pairHost(this, this.databaseConDetails[0], false)
|
|
201
|
+
let external_sources_cons = this.databaseConDetails.slice(1).map(con => {
|
|
202
|
+
let db = new database()
|
|
203
|
+
this.databases.push(db)
|
|
204
|
+
return db.pairHost(this, con)
|
|
205
|
+
})
|
|
208
206
|
|
|
209
|
-
if (this.
|
|
210
|
-
this.username = (<loginOptionsFileMaker>this.props).username
|
|
211
|
-
this.name = this.props.database
|
|
212
|
-
|
|
207
|
+
if (this.databaseConDetails[0].method === "filemaker") {
|
|
213
208
|
fetch(`${this.endpoint}/sessions`, {
|
|
214
209
|
hostname: this.hostname,
|
|
215
210
|
port: 443,
|
|
216
211
|
method: "POST",
|
|
217
212
|
headers: {
|
|
218
213
|
"Content-Type": "application/json",
|
|
219
|
-
"Authorization": "Basic " + Buffer.from(this.username + ":" + (<loginOptionsFileMaker>this.
|
|
220
|
-
}
|
|
214
|
+
"Authorization": "Basic " + Buffer.from((<loginOptionsFileMaker>this.databaseConDetails[0]).username + ":" + (<loginOptionsFileMaker>this.databaseConDetails[0]).password).toString("base64")
|
|
215
|
+
},
|
|
216
|
+
body: JSON.stringify({
|
|
217
|
+
fmDataSource: external_sources_cons
|
|
218
|
+
})
|
|
221
219
|
}).then(async res => {
|
|
222
220
|
if (res.status === 200) {
|
|
223
221
|
this._token = res.headers.get('x-fm-data-access-token')
|
|
@@ -230,22 +228,20 @@ export default class FileMakerConnection extends database{
|
|
|
230
228
|
.catch(e => {
|
|
231
229
|
reject(e)
|
|
232
230
|
})
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
this._token = (<loginOptionsToken>this.props).token
|
|
236
|
-
this.name = this.props.database
|
|
231
|
+
} else if (this.databaseConDetails[0].method === "token") {
|
|
232
|
+
this._token = (<loginOptionsToken>this.databaseConDetails[0]).token
|
|
237
233
|
resolve(this.token)
|
|
238
|
-
}
|
|
239
|
-
else if (this.props.method === "oauth") {
|
|
240
|
-
this.name = this.props.database
|
|
234
|
+
} else if (this.databaseConDetails[0].method === "oauth") {
|
|
241
235
|
fetch(`${this.endpoint}/sessions`, {
|
|
242
236
|
method: "POST",
|
|
243
237
|
headers: {
|
|
244
238
|
"Content-Type": "application/json",
|
|
245
|
-
"X-FM-Data-OAuth-RequestId": (<loginOptionsOAuth>this.
|
|
246
|
-
"X-FM-Data-OAuth-Identifier": (<loginOptionsOAuth>this.
|
|
239
|
+
"X-FM-Data-OAuth-RequestId": (<loginOptionsOAuth>this.databaseConDetails[0]).oauth.requestId,
|
|
240
|
+
"X-FM-Data-OAuth-Identifier": (<loginOptionsOAuth>this.databaseConDetails[0]).oauth.requestIdentifier
|
|
247
241
|
},
|
|
248
|
-
body: "{}"
|
|
242
|
+
body: external_sources_cons.length === 0 ? "{}" : JSON.stringify({
|
|
243
|
+
fmDataSource: external_sources_cons
|
|
244
|
+
})
|
|
249
245
|
})
|
|
250
246
|
.then(res => res.json())
|
|
251
247
|
.then(res => {
|
|
@@ -253,14 +249,12 @@ export default class FileMakerConnection extends database{
|
|
|
253
249
|
this._token = _res.headers["x-fm-data-access-token"]
|
|
254
250
|
resolve(this.token)
|
|
255
251
|
})
|
|
256
|
-
}
|
|
257
|
-
else if (this.props.method === "claris") {
|
|
258
|
-
this.name = this.props.database
|
|
252
|
+
} else if (this.databaseConDetails[0].method === "claris") {
|
|
259
253
|
fetch(`${this.endpoint}/sessions`, {
|
|
260
254
|
method: "POST",
|
|
261
255
|
headers: {
|
|
262
256
|
"Content-Type": "application/json",
|
|
263
|
-
"Authorization": (<loginOptionsClaris>this.
|
|
257
|
+
"Authorization": (<loginOptionsClaris>this.databaseConDetails[0]).claris.fmid,
|
|
264
258
|
},
|
|
265
259
|
body: "{}"
|
|
266
260
|
})
|
|
@@ -274,15 +268,24 @@ export default class FileMakerConnection extends database{
|
|
|
274
268
|
})
|
|
275
269
|
}
|
|
276
270
|
|
|
271
|
+
getDatabase(database_name) {
|
|
272
|
+
return this.databases.find(db => db.name === database_name)
|
|
273
|
+
}
|
|
274
|
+
|
|
277
275
|
get token() {
|
|
278
276
|
return this._token
|
|
279
277
|
}
|
|
278
|
+
|
|
279
|
+
get endpoint(): string {
|
|
280
|
+
return `https://${this.hostname}/fmi/data/v2/databases/`
|
|
281
|
+
}
|
|
280
282
|
}
|
|
281
283
|
|
|
282
284
|
class layout {
|
|
283
285
|
readonly database: database;
|
|
284
286
|
protected name: string;
|
|
285
287
|
metadata: any;
|
|
288
|
+
|
|
286
289
|
constructor(database: database, name: string) {
|
|
287
290
|
this.database = database
|
|
288
291
|
this.name = name
|
|
@@ -357,7 +360,7 @@ class record extends EventEmitter {
|
|
|
357
360
|
fields: field[];
|
|
358
361
|
readonly portals: portal[];
|
|
359
362
|
private portalData: any[];
|
|
360
|
-
|
|
363
|
+
|
|
361
364
|
constructor(layout, recordId, modId = recordId, fieldData = {}, portalData = null) {
|
|
362
365
|
super();
|
|
363
366
|
this.layout = layout
|
|
@@ -399,7 +402,9 @@ class record extends EventEmitter {
|
|
|
399
402
|
}
|
|
400
403
|
|
|
401
404
|
get() {
|
|
402
|
-
if (this.recordId === -1) {
|
|
405
|
+
if (this.recordId === -1) {
|
|
406
|
+
throw "Cannot get this record until a commit() is done."
|
|
407
|
+
}
|
|
403
408
|
return new Promise(async (resolve, reject) => {
|
|
404
409
|
if (!this.layout.metadata) await this.layout.getLayoutMeta()
|
|
405
410
|
|
|
@@ -445,8 +450,7 @@ class record extends EventEmitter {
|
|
|
445
450
|
.then(res => {
|
|
446
451
|
if (typeof res.response.scriptError !== "undefined" && res.response.scriptError !== '0') {
|
|
447
452
|
reject(new FMError(res.response.scriptError, res.status, res))
|
|
448
|
-
}
|
|
449
|
-
else if (res.messages[0].code === "0") {
|
|
453
|
+
} else if (res.messages[0].code === "0") {
|
|
450
454
|
console.log(res)
|
|
451
455
|
this.recordId = parseInt(res.response.recordId)
|
|
452
456
|
this.modId = parseInt(res.response.modId)
|
|
@@ -455,7 +459,7 @@ class record extends EventEmitter {
|
|
|
455
459
|
reject(new FMError(res.messages[0].code, res.status, res))
|
|
456
460
|
}
|
|
457
461
|
})
|
|
458
|
-
|
|
462
|
+
.catch(e => {
|
|
459
463
|
reject(e)
|
|
460
464
|
})
|
|
461
465
|
|
|
@@ -471,8 +475,7 @@ class record extends EventEmitter {
|
|
|
471
475
|
.then(res => {
|
|
472
476
|
if (typeof res.response.scriptError !== "undefined" && res.response.scriptError !== '0') {
|
|
473
477
|
reject(new FMError(res.response.scriptError, res.status, res))
|
|
474
|
-
}
|
|
475
|
-
else if (res.messages[0].code === "0") {
|
|
478
|
+
} else if (res.messages[0].code === "0") {
|
|
476
479
|
console.log(res)
|
|
477
480
|
this.modId = res.response.modId
|
|
478
481
|
this.emit("saved")
|
|
@@ -529,6 +532,7 @@ class field {
|
|
|
529
532
|
id: number;
|
|
530
533
|
value: string | number;
|
|
531
534
|
edited: boolean;
|
|
535
|
+
|
|
532
536
|
constructor(record, id, contents) {
|
|
533
537
|
this.record = record
|
|
534
538
|
this.id = id
|
|
@@ -570,9 +574,12 @@ class field {
|
|
|
570
574
|
reject(new FMError(_res.messages[0].code, _res.status, data))
|
|
571
575
|
}
|
|
572
576
|
})
|
|
573
|
-
.catch(e => {
|
|
577
|
+
.catch(e => {
|
|
578
|
+
reject(e)
|
|
579
|
+
})
|
|
574
580
|
})
|
|
575
581
|
}
|
|
582
|
+
|
|
576
583
|
download() {
|
|
577
584
|
return new Promise((resolve, reject) => {
|
|
578
585
|
https.get(this.value.toString(), (res) => {
|
|
@@ -624,15 +631,16 @@ class portalItem extends record {
|
|
|
624
631
|
}
|
|
625
632
|
}
|
|
626
633
|
|
|
627
|
-
class
|
|
634
|
+
class recordGetOperation {
|
|
628
635
|
protected layout: layout
|
|
629
|
-
protected
|
|
636
|
+
protected limit: number = 100
|
|
630
637
|
protected scripts: object
|
|
631
638
|
protected sort: object[]
|
|
639
|
+
protected limitPortals: limitPortalsInterface[] = []
|
|
640
|
+
protected offset: number = 0
|
|
632
641
|
|
|
633
642
|
constructor(layout) {
|
|
634
643
|
this.layout = layout
|
|
635
|
-
this.queries = []
|
|
636
644
|
this.scripts = {
|
|
637
645
|
"script": null, // Runs after everything
|
|
638
646
|
"script.prerequeset": null, // Runs before the request
|
|
@@ -641,6 +649,86 @@ class find {
|
|
|
641
649
|
this.sort = []
|
|
642
650
|
}
|
|
643
651
|
|
|
652
|
+
/*
|
|
653
|
+
addToPortalLimit() will adjust the results of the get request so that if a layout has multiple portals in it,
|
|
654
|
+
only data from the specified ones will be read. This may help reduce load on your FileMaker API
|
|
655
|
+
*/
|
|
656
|
+
addToPortalLimit(portal: portal, offset = 0, limit = 100) {
|
|
657
|
+
if (offset < 0) throw "Portal offset cannot be less than 0"
|
|
658
|
+
this.limitPortals.push({portal, offset, limit})
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
setLimit(limit: number) {
|
|
662
|
+
if (limit < 1) throw "Record limit too low"
|
|
663
|
+
this.limit = limit
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
addSort(fieldName, sortOrder) {
|
|
667
|
+
this.sort.push({fieldName, sortOrder})
|
|
668
|
+
return this
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
setOffset(offset: number) {
|
|
672
|
+
if (offset < 0) throw "Record offset too low"
|
|
673
|
+
this.limit = offset
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
class recordGetRange extends recordGetOperation {
|
|
678
|
+
constructor(layout) {
|
|
679
|
+
super(layout)
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
private generateQueryParams() {
|
|
683
|
+
let params = []
|
|
684
|
+
if (this.limit !== 100) params.push("_limit=" + this.limit)
|
|
685
|
+
if (this.offset !== 0) params.push("_offset=" + this.offset)
|
|
686
|
+
if (this.sort.length > 0) params.push("_sort=" + encodeURI(JSON.stringify(this.sort)))
|
|
687
|
+
if (this.limitPortals.length > 0) {
|
|
688
|
+
params.push("portal=" + encodeURI(JSON.stringify(this.limitPortals.map(p => p.portal.name))))
|
|
689
|
+
for (let item of this.limitPortals) {
|
|
690
|
+
if (item.offset !== 0) params.push("_offset." + item.portal.name.replace(/[^0-9A-z]/g, "") + "=" + item.offset)
|
|
691
|
+
if (item.limit !== 100) params.push("_limit." + item.portal.name.replace(/[^0-9A-z]/g, "") + "=" + item.limit)
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (params.length === 0) return ""
|
|
696
|
+
return "?" + params.join("&")
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
run() {
|
|
700
|
+
return new Promise((resolve, reject) => {
|
|
701
|
+
// console.log(this.#toObject())
|
|
702
|
+
this.layout.database.apiRequest(`${this.layout.endpoint}/records${this.generateQueryParams()}`, {
|
|
703
|
+
method: "GET"
|
|
704
|
+
}).then(async res => {
|
|
705
|
+
// // console.log(res)
|
|
706
|
+
if (res.messages[0].code === "0") {
|
|
707
|
+
// console.log("RESOLVING")
|
|
708
|
+
if (!this.layout.metadata) await this.layout.getLayoutMeta()
|
|
709
|
+
let data = res.response.data.map(item => {
|
|
710
|
+
return new record(this.layout, item.recordId, item.modId, item.fieldData, item.portalData)
|
|
711
|
+
})
|
|
712
|
+
resolve(data)
|
|
713
|
+
} else {
|
|
714
|
+
reject(new FMError(res.messages[0].code, res.status, res))
|
|
715
|
+
}
|
|
716
|
+
})
|
|
717
|
+
.catch(e => {
|
|
718
|
+
reject(e)
|
|
719
|
+
})
|
|
720
|
+
})
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
class find extends recordGetOperation {
|
|
725
|
+
protected queries: object[]
|
|
726
|
+
|
|
727
|
+
constructor(layout) {
|
|
728
|
+
super(layout)
|
|
729
|
+
this.queries = []
|
|
730
|
+
}
|
|
731
|
+
|
|
644
732
|
private toObject() {
|
|
645
733
|
let out = {query: this.queries, sort: undefined}
|
|
646
734
|
if (this.sort.length !== 0) out.sort = this.sort
|
|
@@ -652,6 +740,16 @@ class find {
|
|
|
652
740
|
}
|
|
653
741
|
}
|
|
654
742
|
|
|
743
|
+
if (this.limit !== 100) out["limit"] = this.limit
|
|
744
|
+
if (this.offset !== 0) out["offset"] = this.offset
|
|
745
|
+
if (this.limitPortals.length > 0) {
|
|
746
|
+
out["portal"] = this.limitPortals.map(p => p.portal.name)
|
|
747
|
+
for (let item of this.limitPortals) {
|
|
748
|
+
out["offset." + item.portal.name.replace(/[^0-9A-z]/g, "")] = item.offset
|
|
749
|
+
out["limit." + item.portal.name.replace(/[^0-9A-z]/g, "")] = item.limit
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
655
753
|
return out
|
|
656
754
|
}
|
|
657
755
|
|
|
@@ -660,12 +758,11 @@ class find {
|
|
|
660
758
|
return this
|
|
661
759
|
}
|
|
662
760
|
|
|
663
|
-
|
|
664
|
-
this.
|
|
665
|
-
return this
|
|
761
|
+
find() {
|
|
762
|
+
return this.run()
|
|
666
763
|
}
|
|
667
764
|
|
|
668
|
-
|
|
765
|
+
run() {
|
|
669
766
|
return new Promise((resolve, reject) => {
|
|
670
767
|
// console.log(this.#toObject())
|
|
671
768
|
this.layout.database.apiRequest(`${this.layout.endpoint}/_find`, {
|