@jd-data-limited/easy-fm 3.2.8 → 4.0.0
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/README.md +36 -24
- package/package.json +12 -9
- package/src/FMError.js +2 -2
- package/src/FMError.ts +2 -2
- package/src/connection/FMHost.d.ts +2 -1
- package/src/connection/FMHost.js +1 -2
- package/src/connection/FMHost.ts +4 -4
- package/src/connection/HostBase.d.ts +1 -1
- package/src/connection/HostBase.js +1 -1
- package/src/connection/HostBase.ts +2 -4
- package/src/connection/database.d.ts +9 -8
- package/src/connection/database.js +6 -7
- package/src/connection/database.ts +14 -13
- package/src/connection/databaseBase.d.ts +4 -4
- package/src/connection/databaseBase.js +1 -1
- package/src/connection/databaseBase.ts +4 -6
- package/src/databaseStructure.d.ts +6 -0
- package/src/databaseStructure.js +4 -0
- package/src/databaseStructure.ts +12 -0
- package/src/index.d.ts +3 -6
- package/src/index.js +3 -5
- package/src/index.ts +4 -9
- package/src/layouts/layout.d.ts +3 -17
- package/src/layouts/layout.js +2 -20
- package/src/layouts/layout.ts +5 -27
- package/src/layouts/layoutBase.d.ts +3 -3
- package/src/layouts/layoutBase.js +1 -1
- package/src/layouts/layoutBase.ts +4 -5
- package/src/layouts/layoutInterface.d.ts +1 -1
- package/src/layouts/layoutInterface.js +1 -1
- package/src/layouts/layoutInterface.ts +2 -2
- package/src/layouts/layoutRecordManager.d.ts +5 -6
- package/src/layouts/layoutRecordManager.js +5 -9
- package/src/layouts/layoutRecordManager.ts +9 -14
- package/src/layouts/recordFieldsMap.d.ts +2 -2
- package/src/layouts/recordFieldsMap.js +1 -1
- package/src/layouts/recordFieldsMap.ts +3 -3
- package/src/records/field.d.ts +3 -23
- package/src/records/field.js +3 -152
- package/src/records/field.ts +4 -169
- package/src/records/fieldBase.d.ts +30 -0
- package/src/records/fieldBase.js +154 -0
- package/src/records/fieldBase.ts +174 -0
- package/src/records/getOperations/recordGetOperation.d.ts +47 -11
- package/src/records/getOperations/recordGetOperation.js +142 -31
- package/src/records/getOperations/recordGetOperation.ts +172 -30
- package/src/records/layoutRecord.d.ts +8 -7
- package/src/records/layoutRecord.js +4 -2
- package/src/records/layoutRecord.ts +19 -11
- package/src/records/portal.d.ts +6 -5
- package/src/records/portal.js +1 -1
- package/src/records/portal.ts +7 -7
- package/src/records/portalBase.d.ts +5 -4
- package/src/records/portalBase.js +1 -1
- package/src/records/portalBase.ts +6 -6
- package/src/records/portalRecord.d.ts +6 -6
- package/src/records/portalRecord.js +3 -2
- package/src/records/portalRecord.ts +5 -5
- package/src/records/recordBase.d.ts +7 -12
- package/src/records/recordBase.js +6 -10
- package/src/records/recordBase.ts +10 -15
- package/src/types.d.ts +18 -15
- package/src/types.js +7 -1
- package/src/types.ts +20 -16
- package/src/records/getOperations/find.d.ts +0 -15
- package/src/records/getOperations/find.js +0 -70
- package/src/records/getOperations/find.ts +0 -77
- package/src/records/getOperations/recordGetRange.d.ts +0 -13
- package/src/records/getOperations/recordGetRange.js +0 -77
- package/src/records/getOperations/recordGetRange.ts +0 -77
- package/src/records/portalRecordBase.d.ts +0 -4
- package/src/records/portalRecordBase.js +0 -6
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2024. See LICENSE file for more information
|
|
3
|
+
*/
|
|
4
|
+
import { DOWNLOAD_MODES, RecordTypes } from "../types.js";
|
|
5
|
+
import { ApiFieldDisplayTypes, ApiFieldResultTypes, ApiFieldTypes } from "../models/apiResults.js";
|
|
6
|
+
export class FieldBase {
|
|
7
|
+
parent;
|
|
8
|
+
id;
|
|
9
|
+
_value;
|
|
10
|
+
edited;
|
|
11
|
+
constructor(record, id, contents) {
|
|
12
|
+
this.parent = record;
|
|
13
|
+
this.id = id;
|
|
14
|
+
this._value = contents;
|
|
15
|
+
this.edited = false;
|
|
16
|
+
}
|
|
17
|
+
set(content) {
|
|
18
|
+
if (this.metadata.result === "container")
|
|
19
|
+
throw "Cannot set container value using set(). Use upload() instead.";
|
|
20
|
+
if ((this.metadata.result === "timeStamp" ||
|
|
21
|
+
this.metadata.result === "date" ||
|
|
22
|
+
this.metadata.result === "time")
|
|
23
|
+
&& !(content instanceof Date) && !!content) {
|
|
24
|
+
throw "Value was not an instance of Date: " + content;
|
|
25
|
+
}
|
|
26
|
+
// @ts-ignore
|
|
27
|
+
if (!content)
|
|
28
|
+
this._value = "";
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
else
|
|
31
|
+
this._value = content;
|
|
32
|
+
this.edited = true;
|
|
33
|
+
}
|
|
34
|
+
get metadata() {
|
|
35
|
+
if (!this.parent.layout.metadata) {
|
|
36
|
+
// Default to a regular text field
|
|
37
|
+
return {
|
|
38
|
+
name: this.id.toString(),
|
|
39
|
+
type: ApiFieldTypes.NORMAL,
|
|
40
|
+
displayType: ApiFieldDisplayTypes.EDIT_TEXT,
|
|
41
|
+
result: ApiFieldResultTypes.TEXT,
|
|
42
|
+
global: false,
|
|
43
|
+
autoEnter: true,
|
|
44
|
+
fourDigitYear: false,
|
|
45
|
+
maxRepeat: 1,
|
|
46
|
+
maxCharacters: 0,
|
|
47
|
+
notEmpty: false,
|
|
48
|
+
numeric: false,
|
|
49
|
+
timeOfDay: false,
|
|
50
|
+
repetitions: 1,
|
|
51
|
+
valueList: ""
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (this.parent.type === RecordTypes.PORTAL) {
|
|
55
|
+
// @ts-ignore
|
|
56
|
+
return this.parent.layout.metadata.portalMetaData[this.parent.portal.name || "portal not attached"].find(i => i.name === this.id) || {
|
|
57
|
+
name: this.id.toString(),
|
|
58
|
+
type: 'normal',
|
|
59
|
+
displayType: 'editText',
|
|
60
|
+
result: 'text',
|
|
61
|
+
global: false,
|
|
62
|
+
autoEnter: true,
|
|
63
|
+
fourDigitYear: false,
|
|
64
|
+
maxRepeat: 1,
|
|
65
|
+
maxCharacters: 0,
|
|
66
|
+
notEmpty: false,
|
|
67
|
+
numeric: false,
|
|
68
|
+
timeOfDay: false,
|
|
69
|
+
repetitionStart: 1,
|
|
70
|
+
repetitionEnd: 1
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
return this.parent.layout.metadata.fieldMetaData.find(i => i.name === this.id) || {
|
|
75
|
+
name: this.id.toString(),
|
|
76
|
+
type: ApiFieldTypes.NORMAL,
|
|
77
|
+
displayType: ApiFieldDisplayTypes.EDIT_TEXT,
|
|
78
|
+
result: ApiFieldResultTypes.TEXT,
|
|
79
|
+
global: false,
|
|
80
|
+
autoEnter: true,
|
|
81
|
+
fourDigitYear: false,
|
|
82
|
+
maxRepeat: 1,
|
|
83
|
+
maxCharacters: 0,
|
|
84
|
+
notEmpty: false,
|
|
85
|
+
numeric: false,
|
|
86
|
+
timeOfDay: false,
|
|
87
|
+
repetitions: 1,
|
|
88
|
+
valueList: ""
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
get value() {
|
|
93
|
+
// if (this.metadata.result === "container") throw "Use await field.stream() to get the contents of a container field, instead of field.value"
|
|
94
|
+
return this._value;
|
|
95
|
+
}
|
|
96
|
+
set value(value) {
|
|
97
|
+
this._value = value;
|
|
98
|
+
}
|
|
99
|
+
get string() {
|
|
100
|
+
if (typeof this._value === "string") {
|
|
101
|
+
return this._value;
|
|
102
|
+
}
|
|
103
|
+
throw "Field value is not a string";
|
|
104
|
+
}
|
|
105
|
+
get date() {
|
|
106
|
+
if (this._value instanceof Date) {
|
|
107
|
+
return this._value;
|
|
108
|
+
}
|
|
109
|
+
throw "Field value is not a date";
|
|
110
|
+
}
|
|
111
|
+
get number() {
|
|
112
|
+
if (typeof this._value === "number") {
|
|
113
|
+
return this._value;
|
|
114
|
+
}
|
|
115
|
+
throw "Field value is not a number";
|
|
116
|
+
}
|
|
117
|
+
async upload(buffer, filename, mime) {
|
|
118
|
+
let trace = new Error();
|
|
119
|
+
if (this.metadata.result !== "container")
|
|
120
|
+
throw "Cannot upload a file to the field; " + this.id + " (not a container field)";
|
|
121
|
+
let form = new FormData();
|
|
122
|
+
form.append("upload", new File([buffer], filename, { type: mime }));
|
|
123
|
+
await this.parent.layout.database.apiRequest(`${this.parent.endpoint}/containers/${this.id}/1`, {
|
|
124
|
+
method: "POST",
|
|
125
|
+
// @ts-ignore
|
|
126
|
+
headers: { "Authorization": "Bearer " + this.parent.layout.database.token },
|
|
127
|
+
body: form
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async download(mode = DOWNLOAD_MODES.Stream) {
|
|
131
|
+
if (this.metadata.result !== ApiFieldResultTypes.CONTAINER)
|
|
132
|
+
throw new Error("Cannot perform download() on a non-container field");
|
|
133
|
+
let stream = await this.parent.layout.database.streamURL(this.string);
|
|
134
|
+
if (mode === DOWNLOAD_MODES.Stream) {
|
|
135
|
+
return stream;
|
|
136
|
+
}
|
|
137
|
+
let body = [];
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
stream.on("data", chunk => {
|
|
140
|
+
body.push(chunk);
|
|
141
|
+
});
|
|
142
|
+
stream.on("error", (e) => {
|
|
143
|
+
reject(e);
|
|
144
|
+
});
|
|
145
|
+
stream.on("end", () => {
|
|
146
|
+
resolve({
|
|
147
|
+
buffer: Buffer.concat(body),
|
|
148
|
+
mime: stream.headers["content-type"],
|
|
149
|
+
request: stream
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2024. See LICENSE file for more information
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as http from "http";
|
|
6
|
+
import {ContainerBufferResult, DOWNLOAD_MODES, FieldMetaData, RecordTypes} from "../types.js";
|
|
7
|
+
import {ApiFieldDisplayTypes, ApiFieldMetadata, ApiFieldResultTypes, ApiFieldTypes} from "../models/apiResults.js";
|
|
8
|
+
import {LayoutBase} from "../layouts/layoutBase.js";
|
|
9
|
+
|
|
10
|
+
export type FieldValue = string | number | Date | Container
|
|
11
|
+
export type Container = null
|
|
12
|
+
|
|
13
|
+
export type Parentable = {layout: LayoutBase, type: RecordTypes, endpoint: string}
|
|
14
|
+
export class FieldBase<T extends FieldValue> {
|
|
15
|
+
parent: Parentable;
|
|
16
|
+
id: string;
|
|
17
|
+
protected _value: T;
|
|
18
|
+
edited: boolean;
|
|
19
|
+
|
|
20
|
+
constructor(record: Parentable, id: string, contents: T) {
|
|
21
|
+
this.parent = record
|
|
22
|
+
this.id = id
|
|
23
|
+
this._value = contents
|
|
24
|
+
this.edited = false
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
set(content: string | number | Date | undefined | null) {
|
|
28
|
+
if (this.metadata.result === "container") throw "Cannot set container value using set(). Use upload() instead."
|
|
29
|
+
if (
|
|
30
|
+
(this.metadata.result === "timeStamp" ||
|
|
31
|
+
this.metadata.result === "date" ||
|
|
32
|
+
this.metadata.result === "time")
|
|
33
|
+
&& !(content instanceof Date) && !!content
|
|
34
|
+
) {
|
|
35
|
+
throw "Value was not an instance of Date: " + content
|
|
36
|
+
}
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
if (!content) this._value = ""
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
else this._value = content
|
|
41
|
+
this.edited = true
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get metadata(): ApiFieldMetadata {
|
|
45
|
+
if (!this.parent.layout.metadata) {
|
|
46
|
+
// Default to a regular text field
|
|
47
|
+
return {
|
|
48
|
+
name: this.id.toString(),
|
|
49
|
+
type: ApiFieldTypes.NORMAL,
|
|
50
|
+
displayType: ApiFieldDisplayTypes.EDIT_TEXT,
|
|
51
|
+
result: ApiFieldResultTypes.TEXT,
|
|
52
|
+
global: false,
|
|
53
|
+
autoEnter: true,
|
|
54
|
+
fourDigitYear: false,
|
|
55
|
+
maxRepeat: 1,
|
|
56
|
+
maxCharacters: 0,
|
|
57
|
+
notEmpty: false,
|
|
58
|
+
numeric: false,
|
|
59
|
+
timeOfDay: false,
|
|
60
|
+
repetitions: 1,
|
|
61
|
+
valueList: ""
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (this.parent.type === RecordTypes.PORTAL) {
|
|
65
|
+
// @ts-ignore
|
|
66
|
+
return this.parent.layout.metadata.portalMetaData[this.parent.portal.name || "portal not attached"].find(i => i.name === this.id) as FieldMetaData || {
|
|
67
|
+
name: this.id.toString(),
|
|
68
|
+
type: 'normal',
|
|
69
|
+
displayType: 'editText',
|
|
70
|
+
result: 'text',
|
|
71
|
+
global: false,
|
|
72
|
+
autoEnter: true,
|
|
73
|
+
fourDigitYear: false,
|
|
74
|
+
maxRepeat: 1,
|
|
75
|
+
maxCharacters: 0,
|
|
76
|
+
notEmpty: false,
|
|
77
|
+
numeric: false,
|
|
78
|
+
timeOfDay: false,
|
|
79
|
+
repetitionStart: 1,
|
|
80
|
+
repetitionEnd: 1
|
|
81
|
+
} as FieldMetaData
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
return this.parent.layout.metadata.fieldMetaData.find(i => i.name === this.id) || {
|
|
85
|
+
name: this.id.toString(),
|
|
86
|
+
type: ApiFieldTypes.NORMAL,
|
|
87
|
+
displayType: ApiFieldDisplayTypes.EDIT_TEXT,
|
|
88
|
+
result: ApiFieldResultTypes.TEXT,
|
|
89
|
+
global: false,
|
|
90
|
+
autoEnter: true,
|
|
91
|
+
fourDigitYear: false,
|
|
92
|
+
maxRepeat: 1,
|
|
93
|
+
maxCharacters: 0,
|
|
94
|
+
notEmpty: false,
|
|
95
|
+
numeric: false,
|
|
96
|
+
timeOfDay: false,
|
|
97
|
+
repetitions: 1,
|
|
98
|
+
valueList: ""
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get value(): T {
|
|
104
|
+
// if (this.metadata.result === "container") throw "Use await field.stream() to get the contents of a container field, instead of field.value"
|
|
105
|
+
return this._value
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
set value(value: T) {
|
|
109
|
+
this._value = value
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get string(): string {
|
|
113
|
+
if (typeof this._value === "string") {
|
|
114
|
+
return this._value
|
|
115
|
+
}
|
|
116
|
+
throw "Field value is not a string"
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get date(): Date {
|
|
120
|
+
if (this._value instanceof Date) {
|
|
121
|
+
return this._value
|
|
122
|
+
}
|
|
123
|
+
throw "Field value is not a date"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
get number(): number {
|
|
127
|
+
if (typeof this._value === "number") {
|
|
128
|
+
return this._value
|
|
129
|
+
}
|
|
130
|
+
throw "Field value is not a number"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async upload(buffer: Buffer, filename: string, mime: string): Promise<void> {
|
|
134
|
+
let trace = new Error()
|
|
135
|
+
if (this.metadata.result !== "container") throw "Cannot upload a file to the field; " + this.id + " (not a container field)"
|
|
136
|
+
let form = new FormData()
|
|
137
|
+
form.append("upload", new File([buffer], filename, {type: mime}))
|
|
138
|
+
|
|
139
|
+
await this.parent.layout.database.apiRequest<null>(`${this.parent.endpoint}/containers/${this.id}/1`, {
|
|
140
|
+
method: "POST",
|
|
141
|
+
// @ts-ignore
|
|
142
|
+
headers: {"Authorization": "Bearer " + this.parent.layout.database.token},
|
|
143
|
+
body: form
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
download(mode: DOWNLOAD_MODES.Stream): Promise<http.IncomingMessage>
|
|
148
|
+
download(mode: DOWNLOAD_MODES.Buffer): Promise<ContainerBufferResult>
|
|
149
|
+
async download(mode: DOWNLOAD_MODES = DOWNLOAD_MODES.Stream): Promise<http.IncomingMessage | ContainerBufferResult> {
|
|
150
|
+
if (this.metadata.result !== ApiFieldResultTypes.CONTAINER) throw new Error("Cannot perform download() on a non-container field")
|
|
151
|
+
|
|
152
|
+
let stream = await this.parent.layout.database.streamURL(this.string)
|
|
153
|
+
if (mode === DOWNLOAD_MODES.Stream) {
|
|
154
|
+
return stream
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let body = []
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
stream.on("data", chunk => {
|
|
160
|
+
body.push(chunk)
|
|
161
|
+
})
|
|
162
|
+
stream.on("error", (e) => {
|
|
163
|
+
reject(e)
|
|
164
|
+
})
|
|
165
|
+
stream.on("end", () => {
|
|
166
|
+
resolve({
|
|
167
|
+
buffer: Buffer.concat(body),
|
|
168
|
+
mime: stream.headers["content-type"],
|
|
169
|
+
request: stream
|
|
170
|
+
} as ContainerBufferResult)
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -1,17 +1,53 @@
|
|
|
1
1
|
import { LayoutInterface } from "../../layouts/layoutInterface.js";
|
|
2
|
-
import {
|
|
3
|
-
import { limitPortalsInterface } from "../../types.js";
|
|
2
|
+
import { ScriptRequestData } from "../../types.js";
|
|
4
3
|
import { LayoutBase } from "../../layouts/layoutBase.js";
|
|
5
|
-
|
|
4
|
+
import { LayoutRecord } from "../layoutRecord.js";
|
|
5
|
+
export type SortOrder = "ascend" | "descend";
|
|
6
|
+
export type FindRequest = {
|
|
7
|
+
[key: string]: string;
|
|
8
|
+
omit?: "true" | "false";
|
|
9
|
+
};
|
|
10
|
+
export type PortalRequest = {
|
|
11
|
+
name: string;
|
|
12
|
+
};
|
|
13
|
+
type PortalData<T extends LayoutInterface> = {
|
|
14
|
+
[key in keyof T["portals"]]: {
|
|
15
|
+
limit: number;
|
|
16
|
+
offset: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
export type GetOperationOptions<T extends LayoutInterface> = {
|
|
20
|
+
portals: Partial<PortalData<T>>;
|
|
21
|
+
limit?: number;
|
|
22
|
+
offset?: number;
|
|
23
|
+
};
|
|
24
|
+
export declare class RecordGetOperation<T extends LayoutInterface, OPTIONS extends GetOperationOptions<T>> {
|
|
6
25
|
protected layout: LayoutBase;
|
|
7
26
|
protected limit: number;
|
|
8
|
-
protected
|
|
9
|
-
protected
|
|
10
|
-
|
|
27
|
+
protected scriptData: ScriptRequestData;
|
|
28
|
+
protected sortData: {
|
|
29
|
+
fieldName: string;
|
|
30
|
+
sortOrder: SortOrder;
|
|
31
|
+
}[];
|
|
32
|
+
protected portals: Partial<PortalData<T>>;
|
|
11
33
|
protected offset: number;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
34
|
+
protected queries: FindRequest[];
|
|
35
|
+
constructor(layout: LayoutBase, options: OPTIONS);
|
|
36
|
+
get isFindRequest(): boolean;
|
|
37
|
+
protected generateParamsBody(offset: number, limit: number): {
|
|
38
|
+
[key: string]: any;
|
|
39
|
+
};
|
|
40
|
+
protected generateParamsURL(offset: number, limit: number): URLSearchParams;
|
|
41
|
+
scripts(scripts: ScriptRequestData): this;
|
|
42
|
+
sort(fieldName: string, sortOrder: SortOrder): this;
|
|
43
|
+
addRequest(query: FindRequest): this;
|
|
44
|
+
fetch(): Promise<LayoutRecord<T, keyof OPTIONS["portals"]>[]>;
|
|
45
|
+
private performFind;
|
|
46
|
+
[Symbol.asyncIterator](): {
|
|
47
|
+
next: () => Promise<{
|
|
48
|
+
done: boolean;
|
|
49
|
+
value: LayoutRecord<T, keyof OPTIONS["portals"]>;
|
|
50
|
+
}>;
|
|
51
|
+
};
|
|
17
52
|
}
|
|
53
|
+
export {};
|
|
@@ -1,46 +1,157 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c) 2023. See LICENSE file for more information
|
|
2
|
+
* Copyright (c) 2023-2024. See LICENSE file for more information
|
|
3
3
|
*/
|
|
4
|
+
import { LayoutRecord } from "../layoutRecord.js";
|
|
5
|
+
import { FMError } from "../../FMError.js";
|
|
4
6
|
export class RecordGetOperation {
|
|
5
7
|
layout;
|
|
6
8
|
limit = 100;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
offset =
|
|
11
|
-
|
|
9
|
+
scriptData = {};
|
|
10
|
+
sortData = [];
|
|
11
|
+
portals;
|
|
12
|
+
offset = 1;
|
|
13
|
+
queries = [];
|
|
14
|
+
constructor(layout, options) {
|
|
12
15
|
this.layout = layout;
|
|
13
|
-
this.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
this.sortData = [];
|
|
17
|
+
this.portals = options.portals;
|
|
18
|
+
this.offset = options.offset || 1; // Offset refers to the starting record. offset 1 is the same as no offset.
|
|
19
|
+
this.limit = options.limit || 100;
|
|
20
|
+
}
|
|
21
|
+
get isFindRequest() {
|
|
22
|
+
return this.queries.length !== 0;
|
|
23
|
+
}
|
|
24
|
+
generateParamsBody(offset, limit) {
|
|
25
|
+
const params = {
|
|
26
|
+
limit: limit.toString(),
|
|
27
|
+
offset: offset.toString(),
|
|
17
28
|
};
|
|
18
|
-
this.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
if (this.sortData.length !== 0)
|
|
30
|
+
params.sort = JSON.stringify(this.sortData);
|
|
31
|
+
if (this.scriptData.after)
|
|
32
|
+
params["script"] = this.scriptData.after.name;
|
|
33
|
+
if (this.scriptData.after?.parameter)
|
|
34
|
+
params["script.param"] = this.scriptData.after.parameter;
|
|
35
|
+
if (this.scriptData.presort)
|
|
36
|
+
params["script.presort"] = this.scriptData.after.name;
|
|
37
|
+
if (this.scriptData.presort?.parameter)
|
|
38
|
+
params["script.presort.param"] = this.scriptData.after.parameter;
|
|
39
|
+
if (this.scriptData.prerequest)
|
|
40
|
+
params["script.prerequest"] = this.scriptData.after.name;
|
|
41
|
+
if (this.scriptData.prerequest?.parameter)
|
|
42
|
+
params["script.prerequest.param"] = this.scriptData.after.parameter;
|
|
43
|
+
if (this.queries.length !== 0)
|
|
44
|
+
params["query"] = this.queries;
|
|
45
|
+
let portals = Object.keys(this.portals);
|
|
46
|
+
params["portal"] = portals;
|
|
47
|
+
for (let portal of portals) {
|
|
48
|
+
params[`offset.${portal}`] = this.portals[portal].offset;
|
|
49
|
+
params[`limit.${portal}`] = this.portals[portal].limit;
|
|
50
|
+
}
|
|
51
|
+
return params;
|
|
29
52
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
53
|
+
generateParamsURL(offset, limit) {
|
|
54
|
+
const params = new URLSearchParams({
|
|
55
|
+
_limit: limit.toString(),
|
|
56
|
+
_offset: offset.toString(),
|
|
57
|
+
});
|
|
58
|
+
if (this.sortData.length !== 0)
|
|
59
|
+
params.set("_sort", JSON.stringify(this.sortData));
|
|
60
|
+
if (this.scriptData.after)
|
|
61
|
+
params.set("script", this.scriptData.after.name);
|
|
62
|
+
if (this.scriptData.after?.parameter)
|
|
63
|
+
params.set("script.param", this.scriptData.after.parameter);
|
|
64
|
+
if (this.scriptData.presort)
|
|
65
|
+
params.set("script.presort", this.scriptData.after.name);
|
|
66
|
+
if (this.scriptData.presort?.parameter)
|
|
67
|
+
params.set("script.presort.param", this.scriptData.after.parameter);
|
|
68
|
+
if (this.scriptData.prerequest)
|
|
69
|
+
params.set("script.prerequest", this.scriptData.after.name);
|
|
70
|
+
if (this.scriptData.prerequest?.parameter)
|
|
71
|
+
params.set("script.prerequest.param", this.scriptData.after.parameter);
|
|
72
|
+
let portals = Object.keys(this.portals);
|
|
73
|
+
for (let portal of portals) {
|
|
74
|
+
params.set(`_offset.${portal}`, this.portals[portal].limit.toString());
|
|
75
|
+
params.set(`_offset.${portal}`, this.portals[portal].offset.toString());
|
|
76
|
+
}
|
|
77
|
+
params.set('portal', JSON.stringify(portals));
|
|
78
|
+
return params;
|
|
79
|
+
}
|
|
80
|
+
scripts(scripts) {
|
|
81
|
+
this.scriptData = scripts;
|
|
34
82
|
return this;
|
|
35
83
|
}
|
|
36
|
-
|
|
37
|
-
this.
|
|
84
|
+
sort(fieldName, sortOrder) {
|
|
85
|
+
this.sortData.push({ fieldName, sortOrder });
|
|
38
86
|
return this;
|
|
39
87
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
throw "Record offset too low";
|
|
43
|
-
this.offset = offset;
|
|
88
|
+
addRequest(query) {
|
|
89
|
+
this.queries.push(query);
|
|
44
90
|
return this;
|
|
45
91
|
}
|
|
92
|
+
fetch() {
|
|
93
|
+
return this.performFind(this.offset, this.limit);
|
|
94
|
+
}
|
|
95
|
+
async performFind(offset, limit) {
|
|
96
|
+
let trace = new Error();
|
|
97
|
+
await this.layout.getLayoutMeta();
|
|
98
|
+
const is_find = this.isFindRequest;
|
|
99
|
+
let endpoint = this.layout.endpoint + (is_find ? "/_find" : "/records");
|
|
100
|
+
if (!is_find)
|
|
101
|
+
endpoint += "?" + new URLSearchParams(this.generateParamsURL(offset, limit)).toString();
|
|
102
|
+
const reqData = {
|
|
103
|
+
// port: 443,
|
|
104
|
+
method: is_find ? "POST" : "GET",
|
|
105
|
+
body: is_find ? JSON.stringify(this.generateParamsBody(offset, limit)) : undefined,
|
|
106
|
+
};
|
|
107
|
+
let res = await this.layout.database.apiRequest(endpoint, reqData);
|
|
108
|
+
if (res.messages[0].code === "0") {
|
|
109
|
+
// console.log("RESOLVING")
|
|
110
|
+
if (!this.layout.metadata)
|
|
111
|
+
await this.layout.getLayoutMeta();
|
|
112
|
+
return res.response.data.map(item => {
|
|
113
|
+
return new LayoutRecord(this.layout, item.recordId, item.modId, item.fieldData, item.portalData);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else if (res.messages[0].code == "401") {
|
|
117
|
+
// No records found, so return empty set
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
throw new FMError(res.messages[0].code, res.httpStatus, res, trace);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
[Symbol.asyncIterator]() {
|
|
125
|
+
let nextOffset = this.offset;
|
|
126
|
+
let startOffset = JSON.parse(JSON.stringify(this.offset));
|
|
127
|
+
let limit = this.limit;
|
|
128
|
+
let exit_after_last_record = false;
|
|
129
|
+
let records = [];
|
|
130
|
+
const fetch = async () => {
|
|
131
|
+
const theoretical_limit = (limit - nextOffset) + startOffset;
|
|
132
|
+
if (theoretical_limit === 0) {
|
|
133
|
+
exit_after_last_record = true;
|
|
134
|
+
records = [];
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
records = await this.performFind(nextOffset, theoretical_limit < 100 ? theoretical_limit : 100);
|
|
138
|
+
nextOffset += 100;
|
|
139
|
+
if (records.length < 100)
|
|
140
|
+
exit_after_last_record = true;
|
|
141
|
+
};
|
|
142
|
+
return {
|
|
143
|
+
next: async () => {
|
|
144
|
+
if (records.length === 0 && !exit_after_last_record) {
|
|
145
|
+
await fetch();
|
|
146
|
+
}
|
|
147
|
+
if (records.length === 0 && exit_after_last_record) {
|
|
148
|
+
return { done: true, value: undefined };
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const record = records.shift();
|
|
152
|
+
return { done: false, value: record };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
46
157
|
}
|