@jd-data-limited/easy-fm 0.0.12 → 1.0.0-alpha1
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/index.js +147 -84
- package/index.ts +206 -90
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -20,79 +20,17 @@ import path from "path";
|
|
|
20
20
|
// @ts-ignore
|
|
21
21
|
import { fileURLToPath } from "url";
|
|
22
22
|
import * as https from "https";
|
|
23
|
-
// import * as btoa from "btoa";
|
|
24
23
|
// @ts-ignore
|
|
25
24
|
const errs = JSON.parse(fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'fmErrors.json')).toString());
|
|
26
|
-
|
|
27
|
-
constructor() {
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
class database {
|
|
26
|
+
constructor(props, host = null) {
|
|
27
|
+
this.props = props;
|
|
28
|
+
this.name = this.props.database;
|
|
29
|
+
if (host)
|
|
30
|
+
this.host = host;
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
-
return
|
|
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 fetch(`${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
|
-
fetch(`${this.endpoint}/sessions`, {
|
|
70
|
-
hostname: this.hostname,
|
|
71
|
-
port: 443,
|
|
72
|
-
method: "POST",
|
|
73
|
-
headers: {
|
|
74
|
-
"Content-Type": "application/json",
|
|
75
|
-
"Authorization": "Basic " + Buffer.from(this.username + ":" + this.password).toString("base64")
|
|
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
|
-
}));
|
|
32
|
+
get token() {
|
|
33
|
+
return this.host.token;
|
|
96
34
|
}
|
|
97
35
|
getLayout(name) {
|
|
98
36
|
return new layout(this, name);
|
|
@@ -102,8 +40,8 @@ export default class FileMakerConnection {
|
|
|
102
40
|
if (!options.headers)
|
|
103
41
|
options.headers = {};
|
|
104
42
|
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;
|
|
43
|
+
options.headers["authorization"] = "Bearer " + this.host.token;
|
|
44
|
+
options.rejectUnauthorized = this.host.rejectUnauthroized;
|
|
107
45
|
let _fetch = yield fetch(url, options);
|
|
108
46
|
let data = yield _fetch.json();
|
|
109
47
|
return data;
|
|
@@ -132,14 +70,141 @@ export default class FileMakerConnection {
|
|
|
132
70
|
script(name, parameter) {
|
|
133
71
|
return { name, parameter };
|
|
134
72
|
}
|
|
73
|
+
get endpoint() {
|
|
74
|
+
return `https://${this.hostname || this.host.hostname}/fmi/data/v2/databases/${encodeURI(this.name)}`;
|
|
75
|
+
}
|
|
76
|
+
get externalLoginInfo() {
|
|
77
|
+
// Returns an object that can be used when using this database object as an external source
|
|
78
|
+
let out = {
|
|
79
|
+
database: this.name,
|
|
80
|
+
username: undefined,
|
|
81
|
+
password: undefined,
|
|
82
|
+
oauthRequestId: undefined,
|
|
83
|
+
oauthIdentifier: undefined
|
|
84
|
+
};
|
|
85
|
+
switch (this.props.method) {
|
|
86
|
+
case "filemaker":
|
|
87
|
+
out.username = this.props.username;
|
|
88
|
+
out.password = this.props.password;
|
|
89
|
+
case "token":
|
|
90
|
+
throw "Token logins cannot be used for connecting to external sources. Please either use no login method, or open a second connection.";
|
|
91
|
+
case "oauth":
|
|
92
|
+
out.oauthRequestId = this.props.oauth.requestId;
|
|
93
|
+
out.oauthIdentifier = this.props.oauth.requestIdentifier;
|
|
94
|
+
case "claris":
|
|
95
|
+
throw "Claris logins cannot be used for connecting to external sources. Please either use no login method, or open a second connection.";
|
|
96
|
+
}
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export default class FileMakerConnection extends database {
|
|
101
|
+
constructor(conn, externalDataSources = [], rejectUnauthorized = true) {
|
|
102
|
+
super(conn.database);
|
|
103
|
+
this.host = this;
|
|
104
|
+
this.hostname = conn.hostname;
|
|
105
|
+
this.externalSources = externalDataSources.map(i => { let e = new database(i, this); e.external = true; return e; });
|
|
106
|
+
this.rejectUnauthroized = rejectUnauthorized;
|
|
107
|
+
}
|
|
108
|
+
logout() {
|
|
109
|
+
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
if (!this.token)
|
|
111
|
+
reject(new Error("Not logged in"));
|
|
112
|
+
let _fetch = yield fetch(`${this.endpoint}/sessions/${this.token}`, {
|
|
113
|
+
method: "DELETE",
|
|
114
|
+
headers: {
|
|
115
|
+
"content-type": "application/json"
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
let data = yield _fetch.json();
|
|
119
|
+
// console.log(data)
|
|
120
|
+
this._token = null;
|
|
121
|
+
this.name = null;
|
|
122
|
+
this.hostname = null;
|
|
123
|
+
resolve();
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
login() {
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
if (this.token)
|
|
129
|
+
throw new Error("Already logged in. Run logout() first");
|
|
130
|
+
if (this.props.method === "filemaker") {
|
|
131
|
+
this.username = this.props.username;
|
|
132
|
+
this.name = this.props.database;
|
|
133
|
+
fetch(`${this.endpoint}/sessions`, {
|
|
134
|
+
hostname: this.hostname,
|
|
135
|
+
port: 443,
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: {
|
|
138
|
+
"Content-Type": "application/json",
|
|
139
|
+
"Authorization": "Basic " + Buffer.from(this.username + ":" + this.props.password).toString("base64")
|
|
140
|
+
}
|
|
141
|
+
}).then((res) => __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
if (res.status === 200) {
|
|
143
|
+
this._token = res.headers.get('x-fm-data-access-token');
|
|
144
|
+
resolve(this._token);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
let _res = (yield res.json());
|
|
148
|
+
reject(new FMError(_res.messages[0].code, _res.status, res));
|
|
149
|
+
}
|
|
150
|
+
}))
|
|
151
|
+
.catch(e => {
|
|
152
|
+
reject(e);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
else if (this.props.method === "token") {
|
|
156
|
+
this._token = this.props.token;
|
|
157
|
+
this.name = this.props.database;
|
|
158
|
+
resolve(this.token);
|
|
159
|
+
}
|
|
160
|
+
else if (this.props.method === "oauth") {
|
|
161
|
+
this.name = this.props.database;
|
|
162
|
+
fetch(`${this.endpoint}/sessions`, {
|
|
163
|
+
method: "POST",
|
|
164
|
+
headers: {
|
|
165
|
+
"Content-Type": "application/json",
|
|
166
|
+
"X-FM-Data-OAuth-RequestId": this.props.oauth.requestId,
|
|
167
|
+
"X-FM-Data-OAuth-Identifier": this.props.oauth.requestIdentifier
|
|
168
|
+
},
|
|
169
|
+
body: "{}"
|
|
170
|
+
})
|
|
171
|
+
.then(res => res.json())
|
|
172
|
+
.then(res => {
|
|
173
|
+
let _res = res;
|
|
174
|
+
this._token = _res.headers["x-fm-data-access-token"];
|
|
175
|
+
resolve(this.token);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
else if (this.props.method === "claris") {
|
|
179
|
+
this.name = this.props.database;
|
|
180
|
+
fetch(`${this.endpoint}/sessions`, {
|
|
181
|
+
method: "POST",
|
|
182
|
+
headers: {
|
|
183
|
+
"Content-Type": "application/json",
|
|
184
|
+
"Authorization": this.props.claris.fmid,
|
|
185
|
+
},
|
|
186
|
+
body: "{}"
|
|
187
|
+
})
|
|
188
|
+
.then(res => res.json())
|
|
189
|
+
.then(res => {
|
|
190
|
+
let _res = res;
|
|
191
|
+
this._token = _res.headers["x-fm-data-access-token"];
|
|
192
|
+
resolve(this.token);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
get token() {
|
|
198
|
+
return this._token;
|
|
199
|
+
}
|
|
135
200
|
}
|
|
136
201
|
class layout {
|
|
137
|
-
constructor(
|
|
138
|
-
this.
|
|
202
|
+
constructor(database, name) {
|
|
203
|
+
this.database = database;
|
|
139
204
|
this.name = name;
|
|
140
205
|
}
|
|
141
206
|
get endpoint() {
|
|
142
|
-
return `${this.
|
|
207
|
+
return `${this.database.endpoint}/layouts/${this.name}`;
|
|
143
208
|
}
|
|
144
209
|
getScript(script) {
|
|
145
210
|
return new script(this, script);
|
|
@@ -152,7 +217,6 @@ class layout {
|
|
|
152
217
|
for (let _field of this.metadata.fieldMetaData) {
|
|
153
218
|
fields[_field.name] = "";
|
|
154
219
|
}
|
|
155
|
-
console.log(fields);
|
|
156
220
|
resolve(new record(this, -1, 0, fields));
|
|
157
221
|
});
|
|
158
222
|
});
|
|
@@ -168,7 +232,7 @@ class layout {
|
|
|
168
232
|
let url = `${this.endpoint}/script/${encodeURI(script.name)}`;
|
|
169
233
|
if (script.parameter)
|
|
170
234
|
url += "?" + encodeURI(script.parameter);
|
|
171
|
-
this.
|
|
235
|
+
this.database.apiRequest(url, {
|
|
172
236
|
port: 443,
|
|
173
237
|
method: "GET"
|
|
174
238
|
})
|
|
@@ -188,7 +252,7 @@ class layout {
|
|
|
188
252
|
}
|
|
189
253
|
getLayoutMeta() {
|
|
190
254
|
return new Promise((resolve, reject) => {
|
|
191
|
-
this.
|
|
255
|
+
this.database.apiRequest(this.endpoint).then(res => {
|
|
192
256
|
this.metadata = res.response;
|
|
193
257
|
resolve(this);
|
|
194
258
|
})
|
|
@@ -240,7 +304,7 @@ class record extends EventEmitter {
|
|
|
240
304
|
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
|
241
305
|
if (!this.layout.metadata)
|
|
242
306
|
yield this.layout.getLayoutMeta();
|
|
243
|
-
this.layout.
|
|
307
|
+
this.layout.database.apiRequest(this.endpoint, {
|
|
244
308
|
port: 443,
|
|
245
309
|
method: "GET"
|
|
246
310
|
})
|
|
@@ -273,7 +337,7 @@ class record extends EventEmitter {
|
|
|
273
337
|
if (this.recordId === -1) {
|
|
274
338
|
// This is a new record
|
|
275
339
|
extraBody.fieldData = data.fieldData;
|
|
276
|
-
this.layout.
|
|
340
|
+
this.layout.database.apiRequest(`${this.layout.endpoint}/records`, {
|
|
277
341
|
port: 443,
|
|
278
342
|
method: "POST",
|
|
279
343
|
body: JSON.stringify(extraBody)
|
|
@@ -281,7 +345,6 @@ class record extends EventEmitter {
|
|
|
281
345
|
.then(res => {
|
|
282
346
|
if (res.messages[0].code === "0") {
|
|
283
347
|
this.recordId = parseInt(res.response.recordId);
|
|
284
|
-
console.log(this.recordId);
|
|
285
348
|
this.modId = parseInt(res.response.modId);
|
|
286
349
|
resolve(this);
|
|
287
350
|
}
|
|
@@ -296,7 +359,7 @@ class record extends EventEmitter {
|
|
|
296
359
|
}
|
|
297
360
|
for (let item of Object.keys(extraBody))
|
|
298
361
|
data[item] = extraBody[item];
|
|
299
|
-
this.layout.
|
|
362
|
+
this.layout.database.apiRequest(this.endpoint, {
|
|
300
363
|
port: 443,
|
|
301
364
|
method: "PATCH",
|
|
302
365
|
body: JSON.stringify(data)
|
|
@@ -376,7 +439,7 @@ class field {
|
|
|
376
439
|
form.append("upload", new File([buffer], filename, { type: mime }));
|
|
377
440
|
let _fetch = yield fetch(`${this.record.endpoint}/containers/${this.id}/1`, {
|
|
378
441
|
method: "POST",
|
|
379
|
-
headers: { "Authorization": "Bearer " + this.record.layout.
|
|
442
|
+
headers: { "Authorization": "Bearer " + this.record.layout.database.token },
|
|
380
443
|
body: form
|
|
381
444
|
}).then(res => res.json())
|
|
382
445
|
.then(data => {
|
|
@@ -468,7 +531,7 @@ class find {
|
|
|
468
531
|
find() {
|
|
469
532
|
return new Promise((resolve, reject) => {
|
|
470
533
|
// console.log(this.#toObject())
|
|
471
|
-
this.layout.
|
|
534
|
+
this.layout.database.apiRequest(`${this.layout.endpoint}/_find`, {
|
|
472
535
|
port: 443,
|
|
473
536
|
method: "POST",
|
|
474
537
|
body: JSON.stringify(this.toObject())
|
package/index.ts
CHANGED
|
@@ -25,6 +25,47 @@ import { fileURLToPath } from "url"
|
|
|
25
25
|
import * as https from "https";
|
|
26
26
|
// import * as btoa from "btoa";
|
|
27
27
|
|
|
28
|
+
interface connectionOptions {
|
|
29
|
+
hostname: string | undefined,
|
|
30
|
+
database: databaseOptions
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface hostConnectionOptions {
|
|
34
|
+
hostname: string
|
|
35
|
+
database: loginOptionsOAuth | loginOptionsFileMaker | loginOptionsClaris | loginOptionsToken
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface databaseOptions {
|
|
39
|
+
method: any
|
|
40
|
+
database: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface loginOptionsToken extends databaseOptions {
|
|
44
|
+
method: "token"
|
|
45
|
+
token: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface loginOptionsOAuth extends databaseOptions {
|
|
49
|
+
method: "oauth"
|
|
50
|
+
oauth: {
|
|
51
|
+
requestId: string,
|
|
52
|
+
requestIdentifier: string
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface loginOptionsFileMaker extends databaseOptions {
|
|
57
|
+
method: "filemaker"
|
|
58
|
+
username: string,
|
|
59
|
+
password: string,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface loginOptionsClaris extends databaseOptions {
|
|
63
|
+
method: "claris"
|
|
64
|
+
claris: {
|
|
65
|
+
fmid: string
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
28
69
|
// @ts-ignore
|
|
29
70
|
const errs = JSON.parse(fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), 'fmErrors.json')).toString())
|
|
30
71
|
|
|
@@ -39,86 +80,23 @@ interface recordObject {
|
|
|
39
80
|
fieldData: any
|
|
40
81
|
}
|
|
41
82
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
public
|
|
45
|
-
public token: any;
|
|
46
|
-
private username: any;
|
|
47
|
-
private password: any;
|
|
48
|
-
private rejectUnauthroized: boolean;
|
|
49
|
-
|
|
50
|
-
constructor() {}
|
|
51
|
-
|
|
52
|
-
get endpoint(): string {
|
|
53
|
-
return `https://${this.hostname}/fmi/data/v2/databases/${encodeURI(this.database)}`
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async login(hostname, database, username, password, rejectUnauthorized = true) {
|
|
57
|
-
if (this.token) throw new Error("Already logged in. Run logout() first")
|
|
83
|
+
class database {
|
|
84
|
+
external: boolean;
|
|
85
|
+
public name: string;
|
|
58
86
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
this.hostname = hostname
|
|
62
|
-
this.database = database
|
|
63
|
-
this.rejectUnauthroized = rejectUnauthorized
|
|
64
|
-
|
|
65
|
-
return await this.createSession()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
logout(): Promise<void> {
|
|
69
|
-
return new Promise(async (resolve, reject) => {
|
|
70
|
-
if (!this.token) reject(new Error("Not logged in"))
|
|
87
|
+
protected host: FileMakerConnection;
|
|
88
|
+
protected props: databaseOptions;
|
|
71
89
|
|
|
72
|
-
|
|
73
|
-
method: "DELETE",
|
|
74
|
-
headers: {
|
|
75
|
-
"content-type": "application/json"
|
|
76
|
-
}
|
|
77
|
-
})
|
|
78
|
-
let data = await _fetch.json()
|
|
79
|
-
// console.log(data)
|
|
80
|
-
this.token = null
|
|
81
|
-
this.database = null
|
|
82
|
-
this.hostname = null
|
|
83
|
-
resolve()
|
|
84
|
-
})
|
|
85
|
-
}
|
|
90
|
+
public hostname: string;
|
|
86
91
|
|
|
87
|
-
|
|
88
|
-
this.
|
|
89
|
-
this.
|
|
90
|
-
this.
|
|
91
|
-
this.rejectUnauthroized = rejectUnauthorized
|
|
92
|
+
constructor(props: databaseOptions, host: FileMakerConnection = null) {
|
|
93
|
+
this.props = props
|
|
94
|
+
this.name = this.props.database
|
|
95
|
+
if (host) this.host = host
|
|
92
96
|
}
|
|
93
97
|
|
|
94
|
-
|
|
95
|
-
return
|
|
96
|
-
fetch(`${this.endpoint}/sessions`, {
|
|
97
|
-
hostname: this.hostname,
|
|
98
|
-
port: 443,
|
|
99
|
-
method: "POST",
|
|
100
|
-
headers: {
|
|
101
|
-
"Content-Type": "application/json",
|
|
102
|
-
"Authorization": "Basic " + Buffer.from(this.username + ":" + this.password).toString("base64")
|
|
103
|
-
}
|
|
104
|
-
}).then(res => {
|
|
105
|
-
console.log(res)
|
|
106
|
-
return res.json()
|
|
107
|
-
})
|
|
108
|
-
.then(res => {
|
|
109
|
-
let _res = res as any
|
|
110
|
-
if (_res.messages[0].code === "0") {
|
|
111
|
-
this.token = _res.response.token
|
|
112
|
-
console.log(_res)
|
|
113
|
-
resolve(this.token)
|
|
114
|
-
} else {
|
|
115
|
-
reject(new FMError(_res.messages[0].code, _res.status, res))
|
|
116
|
-
}
|
|
117
|
-
})
|
|
118
|
-
.catch(e => {
|
|
119
|
-
reject(e)
|
|
120
|
-
})
|
|
121
|
-
})
|
|
98
|
+
get token() {
|
|
99
|
+
return this.host.token
|
|
122
100
|
}
|
|
123
101
|
|
|
124
102
|
getLayout(name): layout {
|
|
@@ -128,8 +106,8 @@ export default class FileMakerConnection {
|
|
|
128
106
|
async apiRequest(url: string | Request, options: any = {}): Promise<any> {
|
|
129
107
|
if (!options.headers) options.headers = {}
|
|
130
108
|
options.headers["content-type"] = options.headers["content-type"] ? options.headers["content-type"] : "application/json"
|
|
131
|
-
options.headers["authorization"] = "Bearer " + this.token
|
|
132
|
-
options.rejectUnauthorized = this.rejectUnauthroized
|
|
109
|
+
options.headers["authorization"] = "Bearer " + this.host.token
|
|
110
|
+
options.rejectUnauthorized = this.host.rejectUnauthroized
|
|
133
111
|
|
|
134
112
|
let _fetch = await fetch(url, options)
|
|
135
113
|
let data = await _fetch.json()
|
|
@@ -159,19 +137,159 @@ export default class FileMakerConnection {
|
|
|
159
137
|
script(name, parameter): Script {
|
|
160
138
|
return ({name, parameter} as Script)
|
|
161
139
|
}
|
|
140
|
+
|
|
141
|
+
get endpoint(): string {
|
|
142
|
+
return `https://${this.hostname || this.host.hostname}/fmi/data/v2/databases/${encodeURI(this.name)}`
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
get externalLoginInfo() {
|
|
146
|
+
// Returns an object that can be used when using this database object as an external source
|
|
147
|
+
let out = {
|
|
148
|
+
database: this.name,
|
|
149
|
+
username: undefined,
|
|
150
|
+
password: undefined,
|
|
151
|
+
oauthRequestId: undefined,
|
|
152
|
+
oauthIdentifier: undefined
|
|
153
|
+
}
|
|
154
|
+
switch (this.props.method) {
|
|
155
|
+
case "filemaker":
|
|
156
|
+
out.username = (<loginOptionsFileMaker>this.props).username
|
|
157
|
+
out.password = (<loginOptionsFileMaker>this.props).password
|
|
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."
|
|
166
|
+
}
|
|
167
|
+
return out
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export default class FileMakerConnection extends database{
|
|
172
|
+
private _token: any;
|
|
173
|
+
private username: any;
|
|
174
|
+
private password: any;
|
|
175
|
+
public readonly rejectUnauthroized: boolean;
|
|
176
|
+
externalSources: database[];
|
|
177
|
+
|
|
178
|
+
constructor(conn: hostConnectionOptions, externalDataSources:databaseOptions[] = [], rejectUnauthorized = true) {
|
|
179
|
+
super(conn.database);
|
|
180
|
+
this.host = this
|
|
181
|
+
this.hostname = conn.hostname
|
|
182
|
+
this.externalSources = externalDataSources.map(i => {let e = new database(i, this); e.external = true; return e;})
|
|
183
|
+
this.rejectUnauthroized = rejectUnauthorized
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
logout(): Promise<void> {
|
|
187
|
+
return new Promise(async (resolve, reject) => {
|
|
188
|
+
if (!this.token) reject(new Error("Not logged in"))
|
|
189
|
+
|
|
190
|
+
let _fetch = await fetch(`${this.endpoint}/sessions/${this.token}`, {
|
|
191
|
+
method: "DELETE",
|
|
192
|
+
headers: {
|
|
193
|
+
"content-type": "application/json"
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
let data = await _fetch.json()
|
|
197
|
+
// console.log(data)
|
|
198
|
+
this._token = null
|
|
199
|
+
this.name = null
|
|
200
|
+
this.hostname = null
|
|
201
|
+
resolve()
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
login() {
|
|
206
|
+
return new Promise<string>((resolve, reject) => {
|
|
207
|
+
if (this.token) throw new Error("Already logged in. Run logout() first")
|
|
208
|
+
|
|
209
|
+
if (this.props.method === "filemaker") {
|
|
210
|
+
this.username = (<loginOptionsFileMaker>this.props).username
|
|
211
|
+
this.name = this.props.database
|
|
212
|
+
|
|
213
|
+
fetch(`${this.endpoint}/sessions`, {
|
|
214
|
+
hostname: this.hostname,
|
|
215
|
+
port: 443,
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers: {
|
|
218
|
+
"Content-Type": "application/json",
|
|
219
|
+
"Authorization": "Basic " + Buffer.from(this.username + ":" + (<loginOptionsFileMaker>this.props).password).toString("base64")
|
|
220
|
+
}
|
|
221
|
+
}).then(async res => {
|
|
222
|
+
if (res.status === 200) {
|
|
223
|
+
this._token = res.headers.get('x-fm-data-access-token')
|
|
224
|
+
resolve(this._token)
|
|
225
|
+
} else {
|
|
226
|
+
let _res = <any>(await res.json())
|
|
227
|
+
reject(new FMError(_res.messages[0].code, _res.status, res))
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
.catch(e => {
|
|
231
|
+
reject(e)
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
else if (this.props.method === "token") {
|
|
235
|
+
this._token = (<loginOptionsToken>this.props).token
|
|
236
|
+
this.name = this.props.database
|
|
237
|
+
resolve(this.token)
|
|
238
|
+
}
|
|
239
|
+
else if (this.props.method === "oauth") {
|
|
240
|
+
this.name = this.props.database
|
|
241
|
+
fetch(`${this.endpoint}/sessions`, {
|
|
242
|
+
method: "POST",
|
|
243
|
+
headers: {
|
|
244
|
+
"Content-Type": "application/json",
|
|
245
|
+
"X-FM-Data-OAuth-RequestId": (<loginOptionsOAuth>this.props).oauth.requestId,
|
|
246
|
+
"X-FM-Data-OAuth-Identifier": (<loginOptionsOAuth>this.props).oauth.requestIdentifier
|
|
247
|
+
},
|
|
248
|
+
body: "{}"
|
|
249
|
+
})
|
|
250
|
+
.then(res => res.json())
|
|
251
|
+
.then(res => {
|
|
252
|
+
let _res = <any>res
|
|
253
|
+
this._token = _res.headers["x-fm-data-access-token"]
|
|
254
|
+
resolve(this.token)
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
else if (this.props.method === "claris") {
|
|
258
|
+
this.name = this.props.database
|
|
259
|
+
fetch(`${this.endpoint}/sessions`, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers: {
|
|
262
|
+
"Content-Type": "application/json",
|
|
263
|
+
"Authorization": (<loginOptionsClaris>this.props).claris.fmid,
|
|
264
|
+
},
|
|
265
|
+
body: "{}"
|
|
266
|
+
})
|
|
267
|
+
.then(res => res.json())
|
|
268
|
+
.then(res => {
|
|
269
|
+
let _res = <any>res
|
|
270
|
+
this._token = _res.headers["x-fm-data-access-token"]
|
|
271
|
+
resolve(this.token)
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
get token() {
|
|
278
|
+
return this._token
|
|
279
|
+
}
|
|
162
280
|
}
|
|
163
281
|
|
|
164
282
|
class layout {
|
|
165
|
-
readonly
|
|
283
|
+
readonly database: database;
|
|
166
284
|
protected name: string;
|
|
167
285
|
metadata: any;
|
|
168
|
-
constructor(
|
|
169
|
-
this.
|
|
286
|
+
constructor(database: database, name: string) {
|
|
287
|
+
this.database = database
|
|
170
288
|
this.name = name
|
|
171
289
|
}
|
|
172
290
|
|
|
173
291
|
get endpoint() {
|
|
174
|
-
return `${this.
|
|
292
|
+
return `${this.database.endpoint}/layouts/${this.name}`
|
|
175
293
|
}
|
|
176
294
|
|
|
177
295
|
getScript(script) {
|
|
@@ -186,7 +304,6 @@ class layout {
|
|
|
186
304
|
for (let _field of this.metadata.fieldMetaData) {
|
|
187
305
|
fields[_field.name] = ""
|
|
188
306
|
}
|
|
189
|
-
console.log(fields)
|
|
190
307
|
resolve(new record(this, -1, 0, fields))
|
|
191
308
|
})
|
|
192
309
|
})
|
|
@@ -204,7 +321,7 @@ class layout {
|
|
|
204
321
|
return new Promise(async (resolve, reject) => {
|
|
205
322
|
let url = `${this.endpoint}/script/${encodeURI(script.name)}`
|
|
206
323
|
if (script.parameter) url += "?" + encodeURI(script.parameter)
|
|
207
|
-
this.
|
|
324
|
+
this.database.apiRequest(url, {
|
|
208
325
|
port: 443,
|
|
209
326
|
method: "GET"
|
|
210
327
|
})
|
|
@@ -224,7 +341,7 @@ class layout {
|
|
|
224
341
|
|
|
225
342
|
public getLayoutMeta(): Promise<layout | FMError> {
|
|
226
343
|
return new Promise((resolve, reject) => {
|
|
227
|
-
this.
|
|
344
|
+
this.database.apiRequest(this.endpoint).then(res => {
|
|
228
345
|
this.metadata = res.response
|
|
229
346
|
resolve(this)
|
|
230
347
|
})
|
|
@@ -286,7 +403,7 @@ class record extends EventEmitter {
|
|
|
286
403
|
return new Promise(async (resolve, reject) => {
|
|
287
404
|
if (!this.layout.metadata) await this.layout.getLayoutMeta()
|
|
288
405
|
|
|
289
|
-
this.layout.
|
|
406
|
+
this.layout.database.apiRequest(this.endpoint, {
|
|
290
407
|
port: 443,
|
|
291
408
|
method: "GET"
|
|
292
409
|
})
|
|
@@ -320,7 +437,7 @@ class record extends EventEmitter {
|
|
|
320
437
|
// This is a new record
|
|
321
438
|
extraBody.fieldData = data.fieldData
|
|
322
439
|
|
|
323
|
-
this.layout.
|
|
440
|
+
this.layout.database.apiRequest(`${this.layout.endpoint}/records`, {
|
|
324
441
|
port: 443,
|
|
325
442
|
method: "POST",
|
|
326
443
|
body: JSON.stringify(extraBody)
|
|
@@ -328,7 +445,6 @@ class record extends EventEmitter {
|
|
|
328
445
|
.then(res => {
|
|
329
446
|
if (res.messages[0].code === "0") {
|
|
330
447
|
this.recordId = parseInt(res.response.recordId)
|
|
331
|
-
console.log(this.recordId)
|
|
332
448
|
this.modId = parseInt(res.response.modId)
|
|
333
449
|
resolve(this)
|
|
334
450
|
} else {
|
|
@@ -343,7 +459,7 @@ class record extends EventEmitter {
|
|
|
343
459
|
}
|
|
344
460
|
|
|
345
461
|
for (let item of Object.keys(extraBody)) data[item] = extraBody[item]
|
|
346
|
-
this.layout.
|
|
462
|
+
this.layout.database.apiRequest(this.endpoint, {
|
|
347
463
|
port: 443,
|
|
348
464
|
method: "PATCH",
|
|
349
465
|
body: JSON.stringify(data)
|
|
@@ -435,7 +551,7 @@ class field {
|
|
|
435
551
|
|
|
436
552
|
let _fetch = await fetch(`${this.record.endpoint}/containers/${this.id}/1`, {
|
|
437
553
|
method: "POST",
|
|
438
|
-
headers: {"Authorization": "Bearer " + this.record.layout.
|
|
554
|
+
headers: {"Authorization": "Bearer " + this.record.layout.database.token},
|
|
439
555
|
body: form
|
|
440
556
|
}).then(res => res.json())
|
|
441
557
|
.then(data => {
|
|
@@ -544,7 +660,7 @@ class find {
|
|
|
544
660
|
find() {
|
|
545
661
|
return new Promise((resolve, reject) => {
|
|
546
662
|
// console.log(this.#toObject())
|
|
547
|
-
this.layout.
|
|
663
|
+
this.layout.database.apiRequest(`${this.layout.endpoint}/_find`, {
|
|
548
664
|
port: 443,
|
|
549
665
|
method: "POST",
|
|
550
666
|
body: JSON.stringify(this.toObject())
|