mobx-lark 2.0.0 → 2.1.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.
@@ -1,301 +1,283 @@
1
- import {
2
- DataObject,
3
- Filter,
4
- ListModel,
5
- PageData,
6
- RESTClient,
7
- Stream,
8
- toggle
9
- } from 'mobx-restful';
10
- import { buildURLData, Constructor, isEmpty } from 'web-utility';
11
-
12
- import { UserIdType } from '../../type';
13
- import { createPageStream } from '../base';
14
- import {
15
- BITable,
16
- TableCellLink,
17
- TableCellRelation,
18
- TableCellText,
19
- TableRecord,
20
- TableRecordData,
21
- TableRecordFields,
22
- TableView
23
- } from './type';
24
-
25
- export * from './type';
26
-
27
- export type FilterOperator = '<' | '<=' | '=' | '!=' | '=>' | '>' | 'contains';
28
-
29
- /**
30
- * @see https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/filter
31
- */
32
- export function makeSimpleFilter(
33
- data: DataObject,
34
- operator: FilterOperator = 'contains',
35
- relation: 'AND' | 'OR' = 'AND'
36
- ) {
37
- const list = Object.entries(data)
38
- .map(
39
- ([key, value]) =>
40
- value != null &&
41
- (value instanceof Array ? value : [value]).map(
42
- (item: string) =>
43
- `CurrentValue.[${key}]` +
44
- (operator === 'contains'
45
- ? `.contains("${item}")`
46
- : `${operator}${JSON.stringify(item)}`)
47
- )
48
- )
49
- .filter(Boolean)
50
- .flat() as string[];
51
-
52
- return list[1] ? `${relation}(${list})` : list[0];
53
- }
54
-
55
- export const normalizeText = (
56
- value: TableCellText | TableCellLink | TableCellRelation
57
- ) =>
58
- (value && typeof value === 'object' && 'text' in value && value.text) || '';
59
-
60
- export type BiBaseData = Omit<TableRecord<{}>, 'record_id' | 'fields'>;
61
-
62
- export interface BiDataQueryOptions {
63
- text_field_as_array?: boolean;
64
- automatic_fields?: boolean;
65
- display_formula_ref?: boolean;
66
- with_shared_url?: boolean;
67
- user_id_type?: UserIdType;
68
- }
69
-
70
- /**
71
- * @see {@link https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview}
72
- */
73
- export function BiDataTable<
74
- T extends DataObject,
75
- F extends Filter<T> = Filter<T>
76
- >(Base = ListModel) {
77
- abstract class BiDataTableModel extends Stream<T, F>(Base) {
78
- requiredKeys: readonly (keyof T)[] = [];
79
-
80
- sort: Partial<Record<keyof T, 'ASC' | 'DESC'>> = {};
81
-
82
- queryOptions: BiDataQueryOptions = {
83
- text_field_as_array: true,
84
- automatic_fields: true
85
- };
86
- currentViewId?: string;
87
-
88
- constructor(appId: string, tableId: string) {
89
- super();
90
- this.baseURI = `bitable/v1/apps/${appId}/tables/${tableId}/records`;
91
- }
92
-
93
- normalize({ fields, ...meta }: TableRecord<T>): T {
94
- return { ...meta, ...fields };
95
- }
96
-
97
- /**
98
- * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/get}
99
- */
100
- @toggle('downloading')
101
- async getOne(id: string) {
102
- const { body } = await this.client.get<TableRecordData<T>>(
103
- `${this.baseURI}/${id}?${buildURLData(this.queryOptions)}`
104
- );
105
- return (this.currentOne = this.normalize(body!.data!.record));
106
- }
107
-
108
- /**
109
- * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/create}
110
- * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/update}
111
- */
112
- @toggle('uploading')
113
- async updateOne(fields: F, id?: string) {
114
- const { body } = await (id
115
- ? this.client.put<TableRecordData<T>>(`${this.baseURI}/${id}`, {
116
- fields
117
- })
118
- : this.client.post<TableRecordData<T>>(this.baseURI, {
119
- fields
120
- }));
121
- return (this.currentOne = this.normalize(body!.data!.record));
122
- }
123
-
124
- makeFilter(filter: F) {
125
- return [
126
- this.requiredKeys[0] &&
127
- makeSimpleFilter(
128
- Object.fromEntries(
129
- this.requiredKeys.map(key => [key, ''])
130
- ),
131
- '!='
132
- ),
133
- !isEmpty(filter) && makeSimpleFilter(filter)
134
- ]
135
- .filter(Boolean)
136
- .join('&&');
137
- }
138
-
139
- /**
140
- * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/list}
141
- */
142
- async *openStream(filter: F) {
143
- const view_id = this.currentViewId;
144
- const searchParams = view_id
145
- ? { view_id }
146
- : {
147
- filter: this.makeFilter(filter),
148
- sort: JSON.stringify(
149
- Object.entries(this.sort).map(
150
- ([key, order]) => `${key} ${order}`
151
- )
152
- )
153
- };
154
- const stream = createPageStream<TableRecord<T>>(
155
- this.client,
156
- this.baseURI,
157
- total => (this.totalCount = total),
158
- { ...searchParams, ...this.queryOptions }
159
- );
160
- for await (const item of stream) yield this.normalize(item);
161
- }
162
-
163
- async getViewList(
164
- viewId: string,
165
- pageIndex = this.pageIndex + 1,
166
- pageSize = this.pageSize
167
- ) {
168
- try {
169
- this.currentViewId = viewId;
170
-
171
- return await this.getList({} as F, pageIndex, pageSize);
172
- } finally {
173
- this.currentViewId = undefined;
174
- }
175
- }
176
-
177
- async getViewAll(viewId: string, pageSize = this.pageSize) {
178
- this.clearList();
179
-
180
- while (!this.noMore)
181
- await this.getViewList(viewId, undefined, pageSize);
182
-
183
- return this.allItems;
184
- }
185
- }
186
- return BiDataTableModel;
187
- }
188
-
189
- export function BiSearch<D extends DataObject, F extends Filter<D> = Filter<D>>(
190
- Model: Constructor<ListModel<D, F>>
191
- ) {
192
- abstract class BiSearchModel extends Model {
193
- declare baseURI: string;
194
- declare client: RESTClient;
195
- declare loadPage: (
196
- pageIndex: number,
197
- pageSize: number,
198
- filter: F
199
- ) => Promise<PageData<D>>;
200
-
201
- abstract searchKeys: readonly (keyof TableRecordFields)[];
202
-
203
- makeFilter(filter: F) {
204
- return isEmpty(filter)
205
- ? ''
206
- : makeSimpleFilter(filter, 'contains', 'OR');
207
- }
208
-
209
- async getSearchList(
210
- keywords: string,
211
- pageIndex = this.pageIndex + 1,
212
- pageSize = this.pageSize
213
- ) {
214
- const wordList = keywords.split(/[\s,]+/);
215
- const filterList = this.searchKeys.map(key => [key, wordList]);
216
-
217
- return this.getList(
218
- Object.fromEntries(filterList),
219
- pageIndex,
220
- pageSize
221
- );
222
- }
223
- }
224
- return BiSearchModel;
225
- }
226
-
227
- interface BiSearchModel
228
- extends InstanceType<ReturnType<typeof BiSearch<TableRecordFields, any>>> {}
229
-
230
- export type BiSearchModelClass = Constructor<BiSearchModel>;
231
-
232
- export function BiTableView() {
233
- abstract class BiTableViewModel extends Stream<TableView>(ListModel) {
234
- constructor(appId: string, tableId: string) {
235
- super();
236
- this.baseURI = `bitable/v1/apps/${appId}/tables/${tableId}/views`;
237
- }
238
-
239
- /**
240
- * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/list}
241
- */
242
- async *openStream() {
243
- for await (const item of createPageStream<TableView>(
244
- this.client,
245
- this.baseURI,
246
- total => (this.totalCount = total)
247
- ))
248
- yield item;
249
- }
250
- }
251
- return BiTableViewModel;
252
- }
253
-
254
- export type BiDataTableClass<
255
- T extends DataObject,
256
- F extends Filter<T> = Filter<T>
257
- > = ReturnType<typeof BiDataTable<T, F>>;
258
-
259
- export function BiTable() {
260
- abstract class BiTableModel extends Stream<BITable>(ListModel) {
261
- constructor(public id: string) {
262
- super();
263
- this.baseURI = `bitable/v1/apps/${id}/tables`;
264
- }
265
-
266
- currentDataTable?: ListModel<any>;
267
-
268
- async getOne<T extends DataObject, F extends Filter<T>>(
269
- tableName: string,
270
- DataTableClass?: BiDataTableClass<T, F>
271
- ) {
272
- const { allItems } = this;
273
-
274
- const list = allItems[0] ? allItems : await this.getAll();
275
-
276
- const table = list.find(({ name }) => name === tableName);
277
-
278
- if (!table) throw new URIError(`Table "${tableName}" is not found`);
279
-
280
- if (DataTableClass instanceof Function)
281
- this.currentDataTable = Reflect.construct(DataTableClass, [
282
- this.id,
283
- table.table_id
284
- ]);
285
- return (this.currentOne = table);
286
- }
287
-
288
- /**
289
- * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list}
290
- */
291
- async *openStream() {
292
- for await (const item of createPageStream<BITable>(
293
- this.client,
294
- this.baseURI,
295
- total => (this.totalCount = total)
296
- ))
297
- yield item;
298
- }
299
- }
300
- return BiTableModel;
301
- }
1
+ import {
2
+ DataObject,
3
+ Filter,
4
+ ListModel,
5
+ PageData,
6
+ RESTClient,
7
+ Stream,
8
+ toggle
9
+ } from 'mobx-restful';
10
+ import { buildURLData, Constructor, isEmpty } from 'web-utility';
11
+
12
+ import { UserIdType } from '../../type';
13
+ import { createPageStream } from '../base';
14
+ import {
15
+ BITable,
16
+ LarkFormData,
17
+ TableFormView,
18
+ TableRecord,
19
+ TableRecordData,
20
+ TableRecordFields,
21
+ TableView
22
+ } from './type';
23
+ import { makeSimpleFilter } from './utility';
24
+
25
+ export * from './type';
26
+ export * from './utility';
27
+
28
+ export type BiBaseData = Omit<
29
+ TableRecord<{}>,
30
+ `record_${'id' | 'url'}` | 'fields'
31
+ >;
32
+
33
+ export interface BiDataQueryOptions {
34
+ text_field_as_array?: boolean;
35
+ automatic_fields?: boolean;
36
+ display_formula_ref?: boolean;
37
+ with_shared_url?: boolean;
38
+ user_id_type?: UserIdType;
39
+ }
40
+
41
+ /**
42
+ * @see {@link https://open.feishu.cn/document/ukTMukTMukTM/uUDN04SN0QjL1QDN/bitable-overview}
43
+ */
44
+ export function BiDataTable<
45
+ T extends DataObject,
46
+ F extends Filter<T> = Filter<T>
47
+ >(Base = ListModel) {
48
+ abstract class BiDataTableModel extends Stream<T, F>(Base) {
49
+ requiredKeys: readonly (keyof T)[] = [];
50
+
51
+ sort: Partial<Record<keyof T, 'ASC' | 'DESC'>> = {};
52
+
53
+ queryOptions: BiDataQueryOptions = {
54
+ text_field_as_array: true,
55
+ automatic_fields: true
56
+ };
57
+ currentViewId?: string;
58
+
59
+ constructor(appId: string, tableId: string) {
60
+ super();
61
+ this.baseURI = `bitable/v1/apps/${appId}/tables/${tableId}/records`;
62
+ }
63
+
64
+ normalize({ fields, ...meta }: TableRecord<T>): T {
65
+ return { ...meta, ...fields };
66
+ }
67
+
68
+ /**
69
+ * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/get}
70
+ */
71
+ @toggle('downloading')
72
+ async getOne(id: string) {
73
+ const { body } = await this.client.get<TableRecordData<T>>(
74
+ `${this.baseURI}/${id}?${buildURLData(this.queryOptions)}`
75
+ );
76
+ return (this.currentOne = this.normalize(body!.data!.record));
77
+ }
78
+
79
+ /**
80
+ * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/create}
81
+ * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/update}
82
+ */
83
+ @toggle('uploading')
84
+ async updateOne(fields: F, id?: string) {
85
+ const { body } = await (id
86
+ ? this.client.put<TableRecordData<T>>(`${this.baseURI}/${id}`, {
87
+ fields
88
+ })
89
+ : this.client.post<TableRecordData<T>>(this.baseURI, {
90
+ fields
91
+ }));
92
+ return (this.currentOne = this.normalize(body!.data!.record));
93
+ }
94
+
95
+ makeFilter(filter: F) {
96
+ return [
97
+ this.requiredKeys[0] &&
98
+ makeSimpleFilter(
99
+ Object.fromEntries(
100
+ this.requiredKeys.map(key => [key, ''])
101
+ ),
102
+ '!='
103
+ ),
104
+ !isEmpty(filter) && makeSimpleFilter(filter)
105
+ ]
106
+ .filter(Boolean)
107
+ .join('&&');
108
+ }
109
+
110
+ /**
111
+ * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/list}
112
+ */
113
+ async *openStream(filter: F) {
114
+ const view_id = this.currentViewId;
115
+ const searchParams = view_id
116
+ ? { view_id }
117
+ : {
118
+ filter: this.makeFilter(filter),
119
+ sort: JSON.stringify(
120
+ Object.entries(this.sort).map(
121
+ ([key, order]) => `${key} ${order}`
122
+ )
123
+ )
124
+ };
125
+ const stream = createPageStream<TableRecord<T>>(
126
+ this.client,
127
+ this.baseURI,
128
+ total => (this.totalCount = total),
129
+ { ...searchParams, ...this.queryOptions }
130
+ );
131
+ for await (const item of stream) yield this.normalize(item);
132
+ }
133
+
134
+ async getViewList(
135
+ viewId: string,
136
+ pageIndex = this.pageIndex + 1,
137
+ pageSize = this.pageSize
138
+ ) {
139
+ try {
140
+ this.currentViewId = viewId;
141
+
142
+ return await this.getList({} as F, pageIndex, pageSize);
143
+ } finally {
144
+ this.currentViewId = undefined;
145
+ }
146
+ }
147
+
148
+ async getViewAll(viewId: string, pageSize = this.pageSize) {
149
+ this.clearList();
150
+
151
+ while (!this.noMore)
152
+ await this.getViewList(viewId, undefined, pageSize);
153
+
154
+ return this.allItems;
155
+ }
156
+ }
157
+ return BiDataTableModel;
158
+ }
159
+
160
+ export function BiSearch<D extends DataObject, F extends Filter<D> = Filter<D>>(
161
+ Model: Constructor<ListModel<D, F>>
162
+ ) {
163
+ abstract class BiSearchModel extends Model {
164
+ declare baseURI: string;
165
+ declare client: RESTClient;
166
+ declare loadPage: (
167
+ pageIndex: number,
168
+ pageSize: number,
169
+ filter: F
170
+ ) => Promise<PageData<D>>;
171
+
172
+ abstract searchKeys: readonly (keyof TableRecordFields)[];
173
+
174
+ makeFilter(filter: F) {
175
+ return isEmpty(filter)
176
+ ? ''
177
+ : makeSimpleFilter(filter, 'contains', 'OR');
178
+ }
179
+
180
+ async getSearchList(
181
+ keywords: string,
182
+ pageIndex = this.pageIndex + 1,
183
+ pageSize = this.pageSize
184
+ ) {
185
+ const wordList = keywords.split(/[\s,]+/);
186
+ const filterList = this.searchKeys.map(key => [key, wordList]);
187
+
188
+ return this.getList(
189
+ Object.fromEntries(filterList),
190
+ pageIndex,
191
+ pageSize
192
+ );
193
+ }
194
+ }
195
+ return BiSearchModel;
196
+ }
197
+
198
+ interface BiSearchModel
199
+ extends InstanceType<ReturnType<typeof BiSearch<TableRecordFields, any>>> {}
200
+
201
+ export type BiSearchModelClass = Constructor<BiSearchModel>;
202
+
203
+ export function BiTableView<
204
+ T extends TableView['view_type'],
205
+ D extends T extends 'form' ? TableFormView : TableView
206
+ >(type = 'grid' as T) {
207
+ abstract class BiTableViewModel extends Stream<D>(ListModel) {
208
+ constructor(appId: string, tableId: string) {
209
+ super();
210
+ this.baseURI = `bitable/v1/apps/${appId}/tables/${tableId}/views`;
211
+ }
212
+
213
+ /**
214
+ * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-view/list}
215
+ * @see {@link https://open.feishu.cn/document/server-docs/docs/bitable-v1/form/get}
216
+ */
217
+ async *openStream() {
218
+ for await (const item of createPageStream<TableView>(
219
+ this.client,
220
+ this.baseURI,
221
+ total => (this.totalCount = total)
222
+ ))
223
+ if (type !== 'form') {
224
+ if (item.view_type === type) yield item as D;
225
+ } else if (item.view_type === 'form') {
226
+ const { body } = await this.client.get<LarkFormData>(
227
+ this.baseURI.replace(/views$/, `forms/${item.view_id}`)
228
+ );
229
+ yield body!.data!.form as D;
230
+ }
231
+ }
232
+ }
233
+ return BiTableViewModel;
234
+ }
235
+
236
+ export type BiDataTableClass<
237
+ T extends DataObject,
238
+ F extends Filter<T> = Filter<T>
239
+ > = ReturnType<typeof BiDataTable<T, F>>;
240
+
241
+ export function BiTable() {
242
+ abstract class BiTableModel extends Stream<BITable>(ListModel) {
243
+ constructor(public id: string) {
244
+ super();
245
+ this.baseURI = `bitable/v1/apps/${id}/tables`;
246
+ }
247
+
248
+ currentDataTable?: ListModel<any>;
249
+
250
+ async getOne<T extends DataObject, F extends Filter<T>>(
251
+ tableName: string,
252
+ DataTableClass?: BiDataTableClass<T, F>
253
+ ) {
254
+ const { allItems } = this;
255
+
256
+ const list = allItems[0] ? allItems : await this.getAll();
257
+
258
+ const table = list.find(({ name }) => name === tableName);
259
+
260
+ if (!table) throw new URIError(`Table "${tableName}" is not found`);
261
+
262
+ if (DataTableClass instanceof Function)
263
+ this.currentDataTable = Reflect.construct(DataTableClass, [
264
+ this.id,
265
+ table.table_id
266
+ ]);
267
+ return (this.currentOne = table);
268
+ }
269
+
270
+ /**
271
+ * @see {@link https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list}
272
+ */
273
+ async *openStream() {
274
+ for await (const item of createPageStream<BITable>(
275
+ this.client,
276
+ this.baseURI,
277
+ total => (this.totalCount = total)
278
+ ))
279
+ yield item;
280
+ }
281
+ }
282
+ return BiTableModel;
283
+ }