rest-client-vue 1.0.0-a1
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/LICENSE +7 -0
- package/README.md +15 -0
- package/dist/rest-client-vue.js +1254 -0
- package/dist/rest-client-vue.umd.cjs +1 -0
- package/dist/src/config.d.ts +12 -0
- package/dist/src/errors.d.ts +5 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/queries.d.ts +95 -0
- package/dist/src/rest-collection-resources.d.ts +30 -0
- package/dist/src/rest-collection.d.ts +308 -0
- package/dist/src/rest-resource.d.ts +140 -0
- package/dist/src/rest-store-registry.d.ts +9 -0
- package/dist/src/stores/rest-collection-store.d.ts +775 -0
- package/dist/src/stores/rest-resource-store.d.ts +277 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +73 -0
@@ -0,0 +1,1254 @@
|
|
1
|
+
var we = Object.defineProperty;
|
2
|
+
var Ce = (f, s, R) => s in f ? we(f, s, { enumerable: !0, configurable: !0, writable: !0, value: R }) : f[s] = R;
|
3
|
+
var re = (f, s, R) => (Ce(f, typeof s != "symbol" ? s + "" : s, R), R), Te = (f, s, R) => {
|
4
|
+
if (!s.has(f))
|
5
|
+
throw TypeError("Cannot " + R);
|
6
|
+
};
|
7
|
+
var Q = (f, s, R) => (Te(f, s, "read from private field"), R ? R.call(f) : s.get(f)), Se = (f, s, R) => {
|
8
|
+
if (s.has(f))
|
9
|
+
throw TypeError("Cannot add the same private member more than once");
|
10
|
+
s instanceof WeakSet ? s.add(f) : s.set(f, R);
|
11
|
+
};
|
12
|
+
import a from "lodash";
|
13
|
+
import { v4 as X } from "uuid";
|
14
|
+
import { ref as I, toRef as te, computed as m, watch as C, nextTick as Le } from "vue";
|
15
|
+
import P from "axios";
|
16
|
+
import me from "jsog";
|
17
|
+
import V from "json-stringify-deterministic";
|
18
|
+
import { defineStore as ye } from "pinia";
|
19
|
+
const pe = {
|
20
|
+
apiBaseUrl: "",
|
21
|
+
useVueLogger: !1
|
22
|
+
};
|
23
|
+
class ke extends Error {
|
24
|
+
constructor(R, L = void 0, e = void 0) {
|
25
|
+
super(R);
|
26
|
+
re(this, "context");
|
27
|
+
re(this, "innerError");
|
28
|
+
this.context = L, this.innerError = e;
|
29
|
+
}
|
30
|
+
}
|
31
|
+
var O;
|
32
|
+
class Ie {
|
33
|
+
constructor() {
|
34
|
+
Se(this, O, {});
|
35
|
+
}
|
36
|
+
clearAllStores() {
|
37
|
+
for (const s in Q(this, O))
|
38
|
+
Q(this, O)[s].clear();
|
39
|
+
}
|
40
|
+
destroyStore(s) {
|
41
|
+
Q(this, O)[s] && (Q(this, O)[s].$dispose(), delete Q(this, O)[s]);
|
42
|
+
}
|
43
|
+
getStore(s) {
|
44
|
+
return Q(this, O)[s];
|
45
|
+
}
|
46
|
+
registerStore(s, R) {
|
47
|
+
Q(this, O)[s] = R;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
O = new WeakMap();
|
51
|
+
function v(f, s) {
|
52
|
+
return a.get(f, (s == null ? void 0 : s.idProperty) || "_id");
|
53
|
+
}
|
54
|
+
function $e(f, s) {
|
55
|
+
var R;
|
56
|
+
return f && ((R = s.filter) != null && R.resourceIds) && !s.order ? s.filter.resourceIds.map((L) => f.find((e) => v(e, s) == L)).filter((L) => L != null) : f;
|
57
|
+
}
|
58
|
+
const De = {
|
59
|
+
detail: {
|
60
|
+
allowMultiple: !0,
|
61
|
+
autoEdit: !1,
|
62
|
+
autoFromMultipleSelection: !0,
|
63
|
+
autoFromSingleInsertion: !0,
|
64
|
+
autoFromSingleSelection: !0,
|
65
|
+
constrainToSelection: !0
|
66
|
+
},
|
67
|
+
filter: {
|
68
|
+
namedFilter: null,
|
69
|
+
query: null,
|
70
|
+
resourceIds: null
|
71
|
+
},
|
72
|
+
idProperty: "_id",
|
73
|
+
limit: 5e3,
|
74
|
+
limitTransientDataToLocalCollection: !0,
|
75
|
+
loading: {
|
76
|
+
firstPageSize: 500,
|
77
|
+
pageSize: 5e3
|
78
|
+
},
|
79
|
+
order: [
|
80
|
+
// TODO Move to SFA LIMS.
|
81
|
+
[{ path: "history.creation.timestamp" }, "desc"],
|
82
|
+
[{ path: "history.creation.orderInBatch" }, "desc"]
|
83
|
+
]
|
84
|
+
}, Y = {
|
85
|
+
resources: [],
|
86
|
+
status: "NotLoaded",
|
87
|
+
selection: [],
|
88
|
+
detailSelection: [],
|
89
|
+
invalidResources: [],
|
90
|
+
transientData: {}
|
91
|
+
}, le = {
|
92
|
+
editingDetailSelection: !1,
|
93
|
+
batchSaveAttempted: !1,
|
94
|
+
editors: [],
|
95
|
+
listNavigators: {}
|
96
|
+
}, Pe = (f, s) => {
|
97
|
+
s = a.merge({}, De, s);
|
98
|
+
const R = ye(f, {
|
99
|
+
state: () => ({
|
100
|
+
idProperty: s.idProperty,
|
101
|
+
filter: s.filter,
|
102
|
+
order: s.order || null,
|
103
|
+
referencePathsToExpand: s.referencePathsToExpand || null,
|
104
|
+
...Y,
|
105
|
+
...le
|
106
|
+
}),
|
107
|
+
getters: {
|
108
|
+
/**
|
109
|
+
* Get the REST query parameters that can be determined from the collection options.
|
110
|
+
*
|
111
|
+
* This includes all query parameters except offset and limit, which vary from request to request.
|
112
|
+
*/
|
113
|
+
fixedQueryParams: (e) => {
|
114
|
+
var l, u, c, d;
|
115
|
+
const r = {};
|
116
|
+
(l = e.filter) != null && l.namedFilter && (r.namedFilter = e.filter.namedFilter);
|
117
|
+
const t = [];
|
118
|
+
if ((u = e.filter) != null && u.resourceIds && t.push({ l: { path: e.idProperty }, r: { constant: e.filter.resourceIds }, operator: "in" }), (c = e.filter) != null && c.query && t.push((d = e.filter) == null ? void 0 : d.query), t.length > 0) {
|
119
|
+
const y = t.length == 1 ? t[0] : { and: t };
|
120
|
+
r.q = V(y);
|
121
|
+
}
|
122
|
+
return e.order && (r.o = V(e.order)), e.referencePathsToExpand && (r.r = V(e.referencePathsToExpand)), r;
|
123
|
+
}
|
124
|
+
},
|
125
|
+
actions: {
|
126
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
127
|
+
// Actions: Configuration
|
128
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
129
|
+
/**
|
130
|
+
* Reset the store to its initial state.
|
131
|
+
*
|
132
|
+
* Configuration is retained, but REST collection contents and contextual state are reset.
|
133
|
+
*/
|
134
|
+
reset() {
|
135
|
+
for (const e in Y)
|
136
|
+
this[e] = Y[e];
|
137
|
+
for (const e in le)
|
138
|
+
this[e] = le[e];
|
139
|
+
},
|
140
|
+
/**
|
141
|
+
* Reset the store to its initial state, but retain transient data if appropriate.
|
142
|
+
*
|
143
|
+
* As with {@link reset}, configuration is retained, but REST collection contents and contextual state are reset.
|
144
|
+
* Transient data are retained if options.limitTransientDataToLocalCollection is false.
|
145
|
+
*/
|
146
|
+
resetRetainingTransientData() {
|
147
|
+
if (s.limitTransientDataToLocalCollection)
|
148
|
+
this.reset();
|
149
|
+
else {
|
150
|
+
const e = this.transientData;
|
151
|
+
this.reset(), this.transientData = e;
|
152
|
+
}
|
153
|
+
},
|
154
|
+
/**
|
155
|
+
* Turn editing mode on or off for the current detail selection.
|
156
|
+
*
|
157
|
+
* Components using this store use the editingDetailSelection state property to govern whether to show an editor for
|
158
|
+
* any current detail selection.
|
159
|
+
*
|
160
|
+
* @param editingDetailSelection The new setting: true if an the detail selection should be shown in an editor,
|
161
|
+
* false if not.
|
162
|
+
*/
|
163
|
+
setEditingDetailSelection(e) {
|
164
|
+
this.editingDetailSelection = e;
|
165
|
+
},
|
166
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
167
|
+
// Actions: Filtering the local collection
|
168
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
169
|
+
/**
|
170
|
+
* Set the filter.
|
171
|
+
*
|
172
|
+
* This function sets the whole filter without triggering a reload of the local collection. More commonly, clients
|
173
|
+
* will call {@link setQuery} or {@link setFilterResourceIds}.
|
174
|
+
*
|
175
|
+
* @param filter The new filter.
|
176
|
+
*/
|
177
|
+
setFilter(e) {
|
178
|
+
a.isEqual(e, this.filter) || (this.filter = e, this.status = "NotLoaded", this.resources = [], this.selection = [], this.detailSelection = [], this.invalidResources = [], this.editingDetailSelection = !1);
|
179
|
+
},
|
180
|
+
/**
|
181
|
+
* Set the filter query.
|
182
|
+
*
|
183
|
+
* If the collection has a state other than NotLoaded, it will be reloaded.
|
184
|
+
*
|
185
|
+
* @param query The new filter query.
|
186
|
+
* @return A promise that resolves when {@link loadResources} completes, or immediately if the collection does not
|
187
|
+
* need to be reloaded.
|
188
|
+
*/
|
189
|
+
async setQuery(e) {
|
190
|
+
var r, t;
|
191
|
+
if (!a.isEqual(e, (r = this.filter) == null ? void 0 : r.query) && (e != null || ((t = this.filter) == null ? void 0 : t.query) != null)) {
|
192
|
+
const l = a.cloneDeep(this.filter) || {};
|
193
|
+
e !== null ? l.query = e : a.unset(l, "query");
|
194
|
+
const u = this.status;
|
195
|
+
this.setFilter(l), u != "NotLoaded" && await this.loadResources();
|
196
|
+
}
|
197
|
+
},
|
198
|
+
/**
|
199
|
+
* Set the list if resource IDs to filter on.
|
200
|
+
*
|
201
|
+
* If the collection has a state other than NotLoaded, it will be reloaded.
|
202
|
+
*
|
203
|
+
* @param resourceIds The list of resource IDs for the new filter.
|
204
|
+
* @return A promise that resolves when {@link loadResources} completes, or immediately if the collection does not
|
205
|
+
* need to be reloaded.
|
206
|
+
*/
|
207
|
+
async setFilterResourceIds(e) {
|
208
|
+
if (!a.isEqual(e, a.get(this.filter, "resourceIds"))) {
|
209
|
+
const r = a.cloneDeep(this.filter) || {};
|
210
|
+
e != null ? r.resourceIds = e : a.unset(r, "resourceIds");
|
211
|
+
const t = this.status;
|
212
|
+
this.setFilter(r), t != "NotLoaded" && await this.loadResources();
|
213
|
+
}
|
214
|
+
},
|
215
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
216
|
+
// Actions: Status
|
217
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
218
|
+
/**
|
219
|
+
* Indicate that a batch save has been attempted and failed.
|
220
|
+
*
|
221
|
+
* Setting this flag is a way of communicating with other client code. The flag should be cleared after further
|
222
|
+
* edits or when another validation or save operations is attempted.
|
223
|
+
*
|
224
|
+
* @param batchSaveAttempted A flag indicating whether a batch save has been attempted and failed.
|
225
|
+
*/
|
226
|
+
setBatchSaveAttempted(e) {
|
227
|
+
e != this.batchSaveAttempted && (this.batchSaveAttempted = e);
|
228
|
+
},
|
229
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
230
|
+
// Actions: Loading and unloading the collection
|
231
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
232
|
+
/**
|
233
|
+
* Clear the collection's contents.
|
234
|
+
*
|
235
|
+
* Configuration and contextual state are retained, but REST collection contents are reset.
|
236
|
+
*/
|
237
|
+
clear() {
|
238
|
+
for (const e in Y)
|
239
|
+
this[e] = Y[e];
|
240
|
+
},
|
241
|
+
/**
|
242
|
+
* Clear the collection's contents, but retain transient data if appropriate.
|
243
|
+
*
|
244
|
+
* As with {@link reset}, configuration and contextual state are retained, but REST collection contents are reset.
|
245
|
+
* Transient data are retained if options.limitTransientDataToLocalCollection is false.
|
246
|
+
*/
|
247
|
+
clearRetainingTransientData() {
|
248
|
+
if (s.limitTransientDataToLocalCollection)
|
249
|
+
this.reset();
|
250
|
+
else {
|
251
|
+
const e = this.transientData;
|
252
|
+
this.reset(), this.transientData = e;
|
253
|
+
}
|
254
|
+
},
|
255
|
+
/**
|
256
|
+
* Load the collection, if it has not been loaded.
|
257
|
+
*
|
258
|
+
* The collection will be loaded if its current status is not Loaded, Loading, LoadingMore, or Failed.
|
259
|
+
*/
|
260
|
+
async ensureCollectionLoaded() {
|
261
|
+
["Loaded", "Loading", "LoadingMore", "Failed"].includes(this.status) || await this.loadResources();
|
262
|
+
},
|
263
|
+
/**
|
264
|
+
* Load the collection.
|
265
|
+
*
|
266
|
+
* If pagination is enabled, this may result in multiple HTTP requests. The function's Promise is resolved once
|
267
|
+
* the first page is loaded.
|
268
|
+
*
|
269
|
+
* @param loadOptions Pagination options that may override the defaults.
|
270
|
+
*/
|
271
|
+
async loadResources(e = {}) {
|
272
|
+
this.resetRetainingTransientData();
|
273
|
+
const r = X();
|
274
|
+
this._loadId = r, await this._loadResources(0, r, e);
|
275
|
+
},
|
276
|
+
/**
|
277
|
+
* Load the collection, optionally starting at some offset.
|
278
|
+
*
|
279
|
+
* If pagination is enabled, this may result in multiple HTTP requests. The function's Promise is resolved once
|
280
|
+
* the first page is loaded.
|
281
|
+
*
|
282
|
+
* @param offset The offset at which to start loading. When loading a whole collection, this will be 0; but if
|
283
|
+
* there are more pages to load, the function will call itself with the next page offset.
|
284
|
+
* @param loadId A unique identifier for this load operation, which is used to detect whether results should be
|
285
|
+
* discarded because a later load operation has been issued. Before calling this method, this identifier should
|
286
|
+
* be written to the store's state property named loadId.
|
287
|
+
* @param loadOptions Pagination options that may override the defaults.
|
288
|
+
*/
|
289
|
+
async _loadResources(e, r, t) {
|
290
|
+
var b, q, A, z, B;
|
291
|
+
const l = e == 0;
|
292
|
+
if (this.status = l ? "Loading" : "LoadingMore", this._loadOffset = e, ((b = this.filter) == null ? void 0 : b.query) === !1) {
|
293
|
+
this.resources = [];
|
294
|
+
return;
|
295
|
+
}
|
296
|
+
const u = this.fixedQueryParams, c = a.cloneDeep(u), d = l ? (q = this.filter) != null && q.resourceIds && !this.order ? null : t.firstPageSize || ((A = s.loading) == null ? void 0 : A.firstPageSize) || t.pageSize || ((z = s.loading) == null ? void 0 : z.pageSize) || null : t.pageSize || ((B = s.loading) == null ? void 0 : B.pageSize) || null, y = d != null || s.limit != null ? a.min(a.filter([d, s.limit], (g) => g != null)) : void 0;
|
297
|
+
y != null ? (c.offset = e, c.limit = y) : e != 0 && (c.offset = e);
|
298
|
+
const U = a.map(
|
299
|
+
c,
|
300
|
+
(g, S) => g != null ? `${encodeURIComponent(S)}=${encodeURIComponent(g)}` : null
|
301
|
+
).filter((g) => g != null).join("&"), T = a.isEmpty(U) ? s.restCollectionUrl : `${s.restCollectionUrl}?${U}`;
|
302
|
+
try {
|
303
|
+
const g = await P.get(T), S = this.fixedQueryParams;
|
304
|
+
if (r != this._loadId || e != this._loadOffset || !a.isEqual(u, S))
|
305
|
+
console.log(`Discarding resources fetched by obsolete query from ${s.restCollectionUrl}.`);
|
306
|
+
else {
|
307
|
+
const E = me.decode(a.get(g, "data.data", [])), $ = (l ? 0 : this.resources.length) + E.length, D = y == null || E.length < y || s.limit && $ >= s.limit;
|
308
|
+
console.log(
|
309
|
+
`Loaded ${E.length}${l ? "" : " more"} resources from ${s.restCollectionUrl}`
|
310
|
+
), l && (this.resources = []);
|
311
|
+
const W = [...this.resources, ...E.map((H) => Object.freeze(H))];
|
312
|
+
this.resources = $e(W, this), this.status = D ? "Loaded" : "LoadingMore", D || this._loadResources(this.resources.length, r, t);
|
313
|
+
}
|
314
|
+
} catch (g) {
|
315
|
+
console.log(`Error while loading resources (URL="${T}"):`, g), this.clearRetainingTransientData(), this.status = "Failed";
|
316
|
+
}
|
317
|
+
},
|
318
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
319
|
+
// Actions: Recording modifications to the collection
|
320
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
321
|
+
/**
|
322
|
+
* Verify that a resource does not exist in the remote collection.
|
323
|
+
*
|
324
|
+
* After checking the remote collection and verifying that the resource does not exist, this function calls
|
325
|
+
* {@link recordDeletion} to delete it from the local collection.
|
326
|
+
*
|
327
|
+
* @param itemId The ID of the deleted resource to check.
|
328
|
+
* @return true if the record does not exist in the remote collection, false if it does or if an error occurred.
|
329
|
+
*/
|
330
|
+
async checkForDeletedResource(e) {
|
331
|
+
let r;
|
332
|
+
try {
|
333
|
+
r = await P.get(`${s.restCollectionUrl}/${e}`);
|
334
|
+
} catch (t) {
|
335
|
+
P.isAxiosError(t) && (r = t.response);
|
336
|
+
}
|
337
|
+
return r && r.status == 404 ? (this.recordDeletion(e), !0) : !1;
|
338
|
+
},
|
339
|
+
/**
|
340
|
+
* Record a deletion, removing the deleted resource from the local collection.
|
341
|
+
*
|
342
|
+
* This should be called after deleting a resource from the remote collection. It is called, for instance, by
|
343
|
+
* {@link deleteResource}; but it can also be called when the application is aware of deletions from the remote
|
344
|
+
* collection made by other means.
|
345
|
+
*
|
346
|
+
* @param resourceId The ID of the resource that has been deleted.
|
347
|
+
*/
|
348
|
+
recordDeletion(e) {
|
349
|
+
var r;
|
350
|
+
if (["Loaded", "LoadingMore"].includes(this.status)) {
|
351
|
+
let t = this.resources.findIndex((l) => v(l, this) == e);
|
352
|
+
t >= 0 && this.resources.splice(t, 1), t = this.selection.findIndex((l) => v(l, this) == e), t >= 0 && this.selection.splice(t, 1), t = this.detailSelection.findIndex((l) => v(l, this) == e), t >= 0 && (this.detailSelection.splice(t, 1), this.detailSelection.length == 0 && (this.editingDetailSelection = ((r = s.detail) == null ? void 0 : r.autoEdit) || !1)), s.limitTransientDataToLocalCollection && delete this.transientData[e];
|
353
|
+
}
|
354
|
+
},
|
355
|
+
/**
|
356
|
+
* Record an insertion, adding the inserted resource to the local collection.
|
357
|
+
*
|
358
|
+
* This should be called after inserting a resource into the remote collection. It is called, for instance, by
|
359
|
+
* {@link saveResource}; but it can also be called when the application is aware of insertions into the remote
|
360
|
+
* collection made by other means.
|
361
|
+
*
|
362
|
+
* It is important that the resources to be added to the local collection have the same object-graph
|
363
|
+
* characteristics as other resources in the local collection. (These characteristics include property inclusions
|
364
|
+
* and exclusions, as well as reference expansions.) To ensure this, you may call {@refreshResource} to fetch the
|
365
|
+
* resource using the collection's settings.
|
366
|
+
*
|
367
|
+
* @param resource The item that has been inserted.
|
368
|
+
* @param TODO
|
369
|
+
*/
|
370
|
+
recordInsertion(e, { insertAtBeginning: r, transientData: t } = {}) {
|
371
|
+
var l;
|
372
|
+
if (["Loaded", "LoadingMore"].includes(this.status)) {
|
373
|
+
const u = Object.freeze(e);
|
374
|
+
r ? this.resources.unshift(u) : this.resources.push(u), (l = s.detail) != null && l.autoFromSingleInsertion && (this.detailSelection.length == 0 || this.detailSelection.length == 1 && v(this.detailSelection[0], this) == null) && (this.detailSelection = [u], this.editingDetailSelection = s.detail.autoEdit);
|
375
|
+
const c = v(e, this);
|
376
|
+
c && (t === void 0 ? delete this.transientData[c] : this.transientData[c] = t);
|
377
|
+
}
|
378
|
+
},
|
379
|
+
/**
|
380
|
+
* Record an update, making the same changes in the local collection.
|
381
|
+
*
|
382
|
+
* This should be called after updating a resource in the remote collection. It is called, for instance, by
|
383
|
+
* {@link saveResource} and {@link refreshResource}; but it can also be called when the application is aware of
|
384
|
+
* updates in the remote collection made by other means.
|
385
|
+
*
|
386
|
+
* It is important that the resource have the same object-graph characteristics as other resources in the local
|
387
|
+
* collection. (These characteristics include property inclusions and exclusions, as well as reference
|
388
|
+
* expansions.) To ensure this, you may instead call {@refreshResource} to fetch the resource using the
|
389
|
+
* collection's settings; this will in turn call {@link recordUpdate}.
|
390
|
+
*
|
391
|
+
* @param resource The item that has been updated.
|
392
|
+
*/
|
393
|
+
recordUpdate(e) {
|
394
|
+
const r = v(e, this);
|
395
|
+
if (r && ["Loaded", "LoadingMore"].includes(this.status)) {
|
396
|
+
const t = Object.freeze(e);
|
397
|
+
let l = this.resources.findIndex((u) => v(u, this) == r);
|
398
|
+
l >= 0 && (this.resources[l] = t), l = this.selection.findIndex((u) => v(u, this) == r), l >= 0 && (this.selection[l] = t), l = this.detailSelection.findIndex((u) => v(u, this) == r), l >= 0 && (this.detailSelection[l] = t);
|
399
|
+
}
|
400
|
+
},
|
401
|
+
/**
|
402
|
+
* Refresh a resource by requesting it from the remote collection.
|
403
|
+
*
|
404
|
+
* If the resource already exists in the local collection, {@link recordUpdate} will be called to update it there.
|
405
|
+
* If not, this method will return the resource but will not add it to the collection.
|
406
|
+
*
|
407
|
+
* This function is called by {@link insertItem} and {@link updateItem} to ensure that the local copy of the added
|
408
|
+
* or updated resource has the same object-graph characteristics as the rest of the local collection.
|
409
|
+
*
|
410
|
+
* @param resourceId The ID of the resource to refresh.
|
411
|
+
* @return The refreshed resource, or null if refreshing failed.
|
412
|
+
*/
|
413
|
+
async refreshResource(e) {
|
414
|
+
if (e) {
|
415
|
+
const r = {};
|
416
|
+
this.referencePathsToExpand && (r.r = V(this.referencePathsToExpand));
|
417
|
+
const t = a.map(
|
418
|
+
r,
|
419
|
+
(c, d) => c != null ? `${encodeURIComponent(d)}=${encodeURIComponent(c)}` : null
|
420
|
+
).filter((c) => c != null).join("&"), l = a.isEmpty(t) ? `${s.restCollectionUrl}/${e}` : `${s.restCollectionUrl}/${e}?${t}`, u = await P.get(l);
|
421
|
+
if (u.status == 200) {
|
422
|
+
const c = u.data;
|
423
|
+
return this.recordUpdate(c), c;
|
424
|
+
}
|
425
|
+
}
|
426
|
+
return null;
|
427
|
+
},
|
428
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
429
|
+
// Actions: Modifying the collection
|
430
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
431
|
+
/**
|
432
|
+
* Insert a new unsaved resource into the local collection and enter editing mode.
|
433
|
+
*
|
434
|
+
* @param resourceDefaults Initial properties of the new resource.
|
435
|
+
*/
|
436
|
+
addResource(e = {}) {
|
437
|
+
this.detailSelection = [a.omit(a.merge({}, e), this.idProperty)], this.editingDetailSelection = !0;
|
438
|
+
},
|
439
|
+
/**
|
440
|
+
* Delete one resource.
|
441
|
+
*
|
442
|
+
* A delete request is sent to the REST API. If it succeeds, the resource is removed from the local collection.
|
443
|
+
*
|
444
|
+
* @param resourceId The ID of the resource to delete.
|
445
|
+
*/
|
446
|
+
async deleteResource(e) {
|
447
|
+
if ((await P.delete(`${s.restCollectionUrl}/${e}`)).status == 200 && ["Loaded", "LoadingMore"].includes(this.status)) {
|
448
|
+
let t = this.resources.findIndex((l) => v(l, this) == e);
|
449
|
+
t >= 0 && this.resources.splice(t, 1), t = this.selection.findIndex((l) => v(l, this) == e), t >= 0 && this.selection.splice(t, 1), t = this.detailSelection.findIndex((l) => v(l, this) == e), t >= 0 && (this.detailSelection.splice(t, 1), this.detailSelection.length == 0 && (this.editingDetailSelection = s.detail.autoEdit)), s.limitTransientDataToLocalCollection && delete this.transientData[e];
|
450
|
+
}
|
451
|
+
},
|
452
|
+
/**
|
453
|
+
* Write a new or updated resource to the remote collection, then mirror the change in the local collection.
|
454
|
+
*
|
455
|
+
* After writing changes, {@link refreshResource} is called to ensure that the local resource (which is also the
|
456
|
+
* return value) has the same object-graph characteristics as the rest of the local collection.
|
457
|
+
*
|
458
|
+
* @param resource The new or updated resource. If it has an ID, an update will be attempted; otherwise it will be
|
459
|
+
* created.
|
460
|
+
* @returns The refreshed local resource after the change has been persisted to the remote collection, or null if
|
461
|
+
* the operation failed.
|
462
|
+
*/
|
463
|
+
async saveResource(e) {
|
464
|
+
const r = v(e, this), t = r == null, l = await P({
|
465
|
+
method: t ? "post" : "put",
|
466
|
+
url: t ? s.restCollectionUrl : `${s.restCollectionUrl}/${r}`,
|
467
|
+
data: e
|
468
|
+
});
|
469
|
+
if (l.status == 200) {
|
470
|
+
const u = l.data, c = v(u, this), d = c ? await this.refreshResource(c) : null;
|
471
|
+
return t && this.recordInsertion(d || Object.freeze(u)), d || u;
|
472
|
+
}
|
473
|
+
return null;
|
474
|
+
},
|
475
|
+
/**
|
476
|
+
* Write new and/or updated resources to the remote collection, and mirror the changes in the local collection.
|
477
|
+
*
|
478
|
+
* After writing changes, {@link refreshResource} is called to ensure that the local resource (which is also the
|
479
|
+
* return value) has the same object-graph characteristics as the rest of the local collection.
|
480
|
+
*
|
481
|
+
* @param resources Resources to create or update. The two operations may be mixed in the list; when a resource
|
482
|
+
* has an ID, an update will be attempted, while otherwise it will be added.
|
483
|
+
* @returns An array of refreshed local resources, one for each remote resource whose save operation succeeded.
|
484
|
+
*/
|
485
|
+
async saveResources(e) {
|
486
|
+
const r = await P({
|
487
|
+
method: "put",
|
488
|
+
url: s.restCollectionUrl,
|
489
|
+
data: e
|
490
|
+
});
|
491
|
+
if (r.status == 200) {
|
492
|
+
const t = r.data || [], l = [];
|
493
|
+
for (const [u, c] of t.entries()) {
|
494
|
+
const d = v(e[u], this) == null, y = v(c, this), U = y ? await this.refreshResource(y) : null;
|
495
|
+
d && this.recordInsertion(U || c), l.push(U || Object.freeze(c));
|
496
|
+
}
|
497
|
+
return l;
|
498
|
+
}
|
499
|
+
return [];
|
500
|
+
},
|
501
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
502
|
+
// Actions: Transient data
|
503
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
504
|
+
/** Clear all transient data. */
|
505
|
+
clearTransientData() {
|
506
|
+
this.transientData = {};
|
507
|
+
},
|
508
|
+
/**
|
509
|
+
* Record which local resources currently fail validation.
|
510
|
+
*
|
511
|
+
* This is useful in multi-resource editing contexts.
|
512
|
+
*
|
513
|
+
* @param resourceIds The IDs of all local resources that failed validation.
|
514
|
+
*/
|
515
|
+
setInvalidResourceIds(e) {
|
516
|
+
a.isEqual(e, this.invalidResources.map((r) => v(r, this))) || (this.invalidResources = e.map((r) => this.resources.find((t) => v(t, this) == r)).filter((r) => r != null));
|
517
|
+
},
|
518
|
+
/**
|
519
|
+
* Set transient data.
|
520
|
+
*
|
521
|
+
* @param transientData The new transient data.
|
522
|
+
* @param merge If true, combine each resource's transient data with existing transient data; if false, replace
|
523
|
+
* the transient data for each resource ID in the keys of transientData.
|
524
|
+
*/
|
525
|
+
setTransientDataForResources(e) {
|
526
|
+
const { transientData: r, merge: t = !0 } = e;
|
527
|
+
t ? a.merge(this.transientData, r) : a.assign(this.transientData, r);
|
528
|
+
},
|
529
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
530
|
+
// Actions: Managing selections
|
531
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
532
|
+
/**
|
533
|
+
* Clear the selection.
|
534
|
+
*/
|
535
|
+
clearSelection() {
|
536
|
+
this.setSelection([]);
|
537
|
+
},
|
538
|
+
/**
|
539
|
+
* Remove resources from the selection.
|
540
|
+
*
|
541
|
+
* @param resourceIds The IDs of resources to remove from the selection.
|
542
|
+
*/
|
543
|
+
deselectResources(e) {
|
544
|
+
const r = this.selection.map((t) => v(t, this)).filter((t) => t != null && !e.includes(t));
|
545
|
+
this.setSelection(r);
|
546
|
+
},
|
547
|
+
/**
|
548
|
+
* Add to or replace the selection.
|
549
|
+
*
|
550
|
+
* @param recordIds The record IDs to select.
|
551
|
+
* @param addToSelection true if the existing selection should be expanded, false if it should be replaced.
|
552
|
+
* @param edit If not undefined, sets the editing mode of the new selection. If undefined, the editing mode will
|
553
|
+
* be left as is, or set to true if options.detail.autoEdit is true.
|
554
|
+
*/
|
555
|
+
selectResources(e, r) {
|
556
|
+
const { addToSelection: t, edit: l } = r || {}, u = this.selection.map((d) => v(d, this));
|
557
|
+
let c = e;
|
558
|
+
if (t) {
|
559
|
+
const d = a.difference(e, u);
|
560
|
+
d.length > 0 && (c = u.concat(d).filter((y) => y != null));
|
561
|
+
}
|
562
|
+
a.isEqual(u, c) || this.setSelection(c, l);
|
563
|
+
},
|
564
|
+
/**
|
565
|
+
* Set a new selection.
|
566
|
+
*
|
567
|
+
* @param selectedResourceIds The resource IDs in the new selection.
|
568
|
+
* @param edit If not undefined, sets the editing mode of the new selection. If undefined, the editing mode will
|
569
|
+
* be left as is, or set to true if options.detail.autoEdit is true.
|
570
|
+
*/
|
571
|
+
setSelection(e, r) {
|
572
|
+
if (!a.isEqual(e, this.selection.map((t) => v(t, this)))) {
|
573
|
+
let t = !0;
|
574
|
+
if (this.editors.length > 0) {
|
575
|
+
if (s.detail.autoFromSingleSelection && this.selection.length == 1)
|
576
|
+
t = !1;
|
577
|
+
else if (s.detail.autoFromMultipleSelection && s.detail.allowMultiple && this.selection.length > 1)
|
578
|
+
t = !1;
|
579
|
+
else if (s.detail.constrainToSelection) {
|
580
|
+
const l = this.selection.map((d) => v(d, this)), c = this.detailSelection.map((d) => v(d, this)).filter((d) => l.includes(d));
|
581
|
+
this.detailSelection.filter((d) => !c.includes(v(d, this))).length > 0 && (t = !1);
|
582
|
+
}
|
583
|
+
}
|
584
|
+
if (!t)
|
585
|
+
return;
|
586
|
+
if (this.selection = e.map((l) => this.resources.find((u) => v(u, this) == l)).filter((l) => l != null), s.detail.autoFromSingleSelection && this.selection.length == 1) {
|
587
|
+
const l = this.selection[0];
|
588
|
+
(this.detailSelection.length != 1 || v(this.detailSelection[0], this) != v(l, this)) && (this.detailSelection = [l]);
|
589
|
+
} else if (s.detail.autoFromMultipleSelection && s.detail.allowMultiple && this.selection.length > 1)
|
590
|
+
a.isEqual(
|
591
|
+
this.selection.map((l) => v(l, this)),
|
592
|
+
this.detailSelection.map((l) => v(l, this))
|
593
|
+
) || (this.detailSelection = a.clone(this.selection));
|
594
|
+
else if (s.detail.constrainToSelection) {
|
595
|
+
const l = this.selection.map((d) => v(d, this)), u = this.detailSelection.map((d) => v(d, this)), c = u.filter((d) => l.includes(d));
|
596
|
+
a.isEqual(c, u) || (this.detailSelection = this.detailSelection.filter((d) => c.includes(v(d, this))));
|
597
|
+
}
|
598
|
+
this.detailSelection.length == 0 ? this.editingDetailSelection = !1 : this.editingDetailSelection = r !== void 0 ? r : this.editingDetailSelection || s.detail.autoEdit;
|
599
|
+
}
|
600
|
+
},
|
601
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
602
|
+
// Actions: Managing the detail view
|
603
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
604
|
+
/**
|
605
|
+
* Hide the detail view.
|
606
|
+
*
|
607
|
+
* This clears detailSelection and sets the detail editing mode to false.
|
608
|
+
*/
|
609
|
+
hideDetail() {
|
610
|
+
this.detailSelection.length > 0 && (this.detailSelection = [], this.editingDetailSelection = !1);
|
611
|
+
},
|
612
|
+
/**
|
613
|
+
* Show the current selection in the detail view.
|
614
|
+
*
|
615
|
+
* If options.detail.allowMultiple is false and more than one resource is selected, the detail view will not be
|
616
|
+
* changed.
|
617
|
+
*
|
618
|
+
* @param edit If not undefined, sets the detail editing mode. If undefined, the editing mode will be left as is,
|
619
|
+
* or set to true if options.detail.autoEdit is true.
|
620
|
+
*/
|
621
|
+
showSelectionAsDetail(e) {
|
622
|
+
if (this.selection.length <= 1 || s.detail.allowMultiple) {
|
623
|
+
const r = this.selection.map((l) => v(l, this)), t = this.detailSelection.map((l) => v(l, this));
|
624
|
+
a.isEqual(r, t) || (this.detailSelection = a.clone(this.selection)), this.detailSelection.length == 0 ? this.editingDetailSelection = !1 : this.editingDetailSelection = e !== void 0 ? e : this.editingDetailSelection || s.detail.autoEdit;
|
625
|
+
}
|
626
|
+
},
|
627
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
628
|
+
// Actions: Tracking editors
|
629
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
630
|
+
/**
|
631
|
+
* Deregister an editor.
|
632
|
+
*
|
633
|
+
* @param editor The editor to deregister.
|
634
|
+
*/
|
635
|
+
deregisterEditor(e) {
|
636
|
+
a.remove(this.editors, (r) => r == e);
|
637
|
+
},
|
638
|
+
/**
|
639
|
+
* Register an editor.
|
640
|
+
*
|
641
|
+
* @param editor The editor to register.
|
642
|
+
*/
|
643
|
+
registerEditor(e) {
|
644
|
+
this.editors.includes(e) || this.editors.push(e);
|
645
|
+
},
|
646
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
647
|
+
// Actions: Tracking list navigators
|
648
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
649
|
+
/**
|
650
|
+
* Deregister a list navigator.
|
651
|
+
*
|
652
|
+
* @param name The name of the list navigator to deregister.
|
653
|
+
* @param listNavigator The list navigator. If defined, the named list navigator will only be deleted if it equals
|
654
|
+
* this parameter.
|
655
|
+
*/
|
656
|
+
deregisterListNavigator({ name: e, listNavigator: r }) {
|
657
|
+
(r == null || this.listNavigators[e] == r) && delete this.listNavigators[e];
|
658
|
+
},
|
659
|
+
/**
|
660
|
+
* Register a list navigator.
|
661
|
+
*
|
662
|
+
* Registering a component such as a list view as a list navigator can help other components with list navigation.
|
663
|
+
* For instance, code that implements a "next" button in a detail view might use the list navigator to determine
|
664
|
+
* what resource to select next.
|
665
|
+
*
|
666
|
+
* If another list navigator is registered under this name, its registration will be replaced.
|
667
|
+
*
|
668
|
+
* @param name The name under which to register the list navigator.
|
669
|
+
* @param listNavigator The list navigator.
|
670
|
+
*/
|
671
|
+
registerListNavigator({ name: e, listNavigator: r }) {
|
672
|
+
this.listNavigators[e] != r && (this.listNavigators[e] = r);
|
673
|
+
}
|
674
|
+
}
|
675
|
+
});
|
676
|
+
return (() => {
|
677
|
+
const e = (r, t) => {
|
678
|
+
const l = R(r, t);
|
679
|
+
return ie.registerStore(l.$id, l), l;
|
680
|
+
};
|
681
|
+
return e.$id = R.$id, e;
|
682
|
+
})();
|
683
|
+
}, ie = new Ie(), Be = (f = {}) => {
|
684
|
+
var oe, ce, ue, de;
|
685
|
+
const {
|
686
|
+
collectionId: s,
|
687
|
+
draftBatchId: R,
|
688
|
+
enabled: L,
|
689
|
+
resourceType: e,
|
690
|
+
options: r
|
691
|
+
} = f, t = I(void 0), l = I(s), u = I(R), c = I(L ?? !0), d = I([te(e)]), y = I(r), U = m(() => u.value), T = I(c.value), b = I(X()), q = I((ce = (oe = y.value) == null ? void 0 : oe.filter) == null ? void 0 : ce.resourceIds), A = I((de = (ue = y.value) == null ? void 0 : ue.filter) == null ? void 0 : de.query), z = m(
|
692
|
+
() => u.value == null && l.value != null && ie.getStore(l.value) != null
|
693
|
+
), B = I(z.value), g = m(() => $("status", "Uninitialized"));
|
694
|
+
function S(i) {
|
695
|
+
if (t.value)
|
696
|
+
return i(t.value);
|
697
|
+
}
|
698
|
+
async function E(i) {
|
699
|
+
if (t.value)
|
700
|
+
return await i(t.value);
|
701
|
+
}
|
702
|
+
function $(i, o) {
|
703
|
+
if (t.value)
|
704
|
+
return t.value[i];
|
705
|
+
if (o !== void 0)
|
706
|
+
return o;
|
707
|
+
throw "Attempted to use a REST collection store that has not been created, with no default value.";
|
708
|
+
}
|
709
|
+
const D = (i = {}) => {
|
710
|
+
var fe, ve, ge, Re;
|
711
|
+
const {
|
712
|
+
collectionId: o,
|
713
|
+
draftBatchId: w,
|
714
|
+
enabled: _,
|
715
|
+
resourceType: se,
|
716
|
+
options: he
|
717
|
+
} = i;
|
718
|
+
l.value = o, u.value = w, c.value = _ ?? !0, d.value = [te(se)], he != null && (y.value = he, q.value = (ve = (fe = y.value) == null ? void 0 : fe.filter) == null ? void 0 : ve.resourceIds, A.value = (Re = (ge = y.value) == null ? void 0 : ge.filter) == null ? void 0 : Re.query), b.value = X(), W(c.value);
|
719
|
+
}, W = async (i) => {
|
720
|
+
i != T.value && (T.value = i, i && (K(), N()));
|
721
|
+
}, H = async (i) => {
|
722
|
+
q.value = i, await E(async (o) => await o.setFilterResourceIds(q.value || null));
|
723
|
+
}, J = async (i) => {
|
724
|
+
if (A.value = i, g.value != "Uninitialized") {
|
725
|
+
const o = i, w = m(() => a.isFunction(o) ? p.value ? o(p.value) : null : o);
|
726
|
+
await E((_) => _.setQuery(w.value || null));
|
727
|
+
}
|
728
|
+
}, K = () => {
|
729
|
+
y.value = a.merge({}, y.value || {}, {
|
730
|
+
filter: { resourceIds: q.value, query: A.value }
|
731
|
+
});
|
732
|
+
}, p = m(
|
733
|
+
() => d.value[0].value || void 0
|
734
|
+
), k = function(i) {
|
735
|
+
const o = a.get(i, "filter.query"), w = a.get(i, "referencePathsToExpand"), _ = a.isFunction(o) ? o(p.value) : o, se = p.value ? a.isFunction(w) ? w(p.value) : w || [] : [];
|
736
|
+
return {
|
737
|
+
...a.cloneDeep(i || {}),
|
738
|
+
filter: {
|
739
|
+
...a.cloneDeep((i == null ? void 0 : i.filter) || {}),
|
740
|
+
query: _
|
741
|
+
},
|
742
|
+
referencePathsToExpand: se
|
743
|
+
};
|
744
|
+
}, x = m(() => k(y.value)), M = m(() => {
|
745
|
+
if (!p.value)
|
746
|
+
return null;
|
747
|
+
if (x.value.restCollectionUrl !== void 0)
|
748
|
+
return x.value.restCollectionUrl;
|
749
|
+
if (!B.value)
|
750
|
+
return !p.value || !p.value.collectionName ? null : U.value ? `${ee.config.apiBaseUrl}/draft-batches/${U.value}/${p.value.collectionName}` : `${ee.config.apiBaseUrl}/${p.value.collectionName}`;
|
751
|
+
}), n = m(() => {
|
752
|
+
var i;
|
753
|
+
if (p.value)
|
754
|
+
return a.mergeWith(
|
755
|
+
a.cloneDeep(De),
|
756
|
+
x.value,
|
757
|
+
{
|
758
|
+
idProperty: (i = p.value) == null ? void 0 : i.idProperty,
|
759
|
+
restCollectionUrl: M.value || ""
|
760
|
+
},
|
761
|
+
(o, w) => {
|
762
|
+
if (a.isArray(w) && w.length == 0 && (o == null || a.isArray(o)))
|
763
|
+
return w;
|
764
|
+
}
|
765
|
+
);
|
766
|
+
}), h = I(0), F = m(
|
767
|
+
() => {
|
768
|
+
var i;
|
769
|
+
return h.value, l.value || p.value && ((i = p.value) == null ? void 0 : i.collectionName) && (U.value ? `draftBatches/${U.value}/${p.value.collectionName}/${b.value}` : `${p.value.collectionName}/${b.value}`) || null;
|
770
|
+
}
|
771
|
+
), N = () => {
|
772
|
+
if (T.value) {
|
773
|
+
let i = !1;
|
774
|
+
return p.value && F.value && (t.value = ie.getStore(F.value), t.value ? i = !1 : n.value && (B.value = !1, t.value = Pe(F.value, n.value)(), i = !0)), i;
|
775
|
+
} else
|
776
|
+
return !1;
|
777
|
+
}, G = () => {
|
778
|
+
F.value && l.value == F.value ? (t.value = void 0, N()) : (t.value = void 0, h.value = h.value + 1, N());
|
779
|
+
};
|
780
|
+
C(U, (i, o) => {
|
781
|
+
a.isEqual(i, o) || G();
|
782
|
+
}), C(T, (i, o) => {
|
783
|
+
i != o && (i ? N() : t.value = void 0);
|
784
|
+
}), C(p, (i, o) => {
|
785
|
+
i && !o ? N() : !i && o || a.isEqual(i, o) || G();
|
786
|
+
}), C(n, (i, o) => {
|
787
|
+
a.isEqual(i, o) || G();
|
788
|
+
}), C(z, () => {
|
789
|
+
z.value || (B.value = !1);
|
790
|
+
}), C(F, () => N()), C(M, (i, o) => {
|
791
|
+
a.isEqual(i, o) || G();
|
792
|
+
});
|
793
|
+
const j = [], Ee = function(i) {
|
794
|
+
g.value != "Uninitialized" ? Le(i) : j.push(i);
|
795
|
+
};
|
796
|
+
return C(g, (i, o) => {
|
797
|
+
if (i != "Uninitialized" && o == "Uninitialized") {
|
798
|
+
const w = a.clone(j);
|
799
|
+
j.splice(0, j.length);
|
800
|
+
for (const _ of w)
|
801
|
+
_();
|
802
|
+
}
|
803
|
+
}), N(), {
|
804
|
+
// Configuration
|
805
|
+
collectionId: F,
|
806
|
+
draftBatchId: U,
|
807
|
+
resourceType: p,
|
808
|
+
// Status
|
809
|
+
enabled: T,
|
810
|
+
status: g,
|
811
|
+
batchSaveAttempted: m(() => $("batchSaveAttempted", !1)),
|
812
|
+
// Resources loaded from the REST API
|
813
|
+
invalidResources: m(() => $("invalidResources", [])),
|
814
|
+
resources: m(() => g.value != "Uninitialized" ? (E((i) => i.ensureCollectionLoaded()), $("resources", [])) : []),
|
815
|
+
// Transient data
|
816
|
+
transientData: m(() => $("transientData", [])),
|
817
|
+
// Selection and detail selection
|
818
|
+
detailSelection: m(() => $("detailSelection", [])),
|
819
|
+
editingDetailSelection: m(() => $("editingDetailSelection", !1)),
|
820
|
+
selection: m(() => $("selection", [])),
|
821
|
+
// Methods: Configuration
|
822
|
+
reconfigureCollection: D,
|
823
|
+
setEditingDetailSelection: (i) => S(
|
824
|
+
(o) => o.setEditingDetailSelection(i)
|
825
|
+
),
|
826
|
+
setEnabled: W,
|
827
|
+
setFilterResourceIds: H,
|
828
|
+
setQuery: J,
|
829
|
+
// Methods: Status
|
830
|
+
ensureStore: N,
|
831
|
+
// TODO Rename
|
832
|
+
setBatchSaveAttempted: (i) => S((o) => o.setBatchSaveAttempted(i)),
|
833
|
+
// Local collection management
|
834
|
+
clear: () => S((i) => i.clear()),
|
835
|
+
ensureCollectionLoaded: async () => await E(async (i) => i.ensureCollectionLoaded()),
|
836
|
+
loadResources: async () => await E(async (i) => await i.loadResources()),
|
837
|
+
// Methods: Recording changes to the collection
|
838
|
+
addResource: (i) => S((o) => o.addResource(i)),
|
839
|
+
checkForDeletedResource: async (i) => await E(async (o) => await o.checkForDeletedResource(i)) == !0,
|
840
|
+
recordDeletion: (i) => S((o) => o.recordDeletion(i)),
|
841
|
+
recordInsertion: (i, { insertAtBeginning: o = !1, transientData: w = void 0 } = {}) => S((_) => _.recordInsertion(i, { insertAtBeginning: o, transientData: w })),
|
842
|
+
recordUpdate: (i) => S((o) => o.recordUpdate(i)),
|
843
|
+
refreshResource: async (i) => await E(
|
844
|
+
async (o) => await o.refreshResource(i)
|
845
|
+
),
|
846
|
+
// Methods: Modifying the collection
|
847
|
+
deleteResource: async (i) => await E(
|
848
|
+
async (o) => await o.deleteResource(i)
|
849
|
+
),
|
850
|
+
saveResource: async (i) => await E(async (o) => await o.saveResource(i)),
|
851
|
+
saveResources: async (i) => await E(async (o) => await o.saveResources(i)),
|
852
|
+
// Methods: Transient data
|
853
|
+
clearTransientData: () => S((i) => i.clearTransientData()),
|
854
|
+
setInvalidResourceIds: (i) => S((o) => o.setInvalidResourceIds(i)),
|
855
|
+
setTransientDataForResources: (i) => S(
|
856
|
+
(o) => o.setTransientDataForResources(i)
|
857
|
+
),
|
858
|
+
// Methods: Selection and detail selection
|
859
|
+
clearSelection: () => S((i) => i.clearSelection()),
|
860
|
+
deselectResources: (i) => S((o) => o.deselectResources(i)),
|
861
|
+
selectResources: (i, o) => S(
|
862
|
+
(w) => w.selectResources(i, o)
|
863
|
+
),
|
864
|
+
// Methods: Managing the detail view
|
865
|
+
hideDetail: () => S((i) => i.hideDetail()),
|
866
|
+
// Methods: Editors
|
867
|
+
deregisterEditor: (i) => S((o) => o.deregisterEditor(i)),
|
868
|
+
registerEditor: (i) => S((o) => o.registerEditor(i)),
|
869
|
+
// Methods: List navigators
|
870
|
+
deregisterListNavigator: (i) => S((o) => o.deregisterListNavigator(i)),
|
871
|
+
registerListNavigator: (i) => S((o) => o.registerListNavigator(i)),
|
872
|
+
// Callbacks
|
873
|
+
onItemsStoreReady: Ee
|
874
|
+
};
|
875
|
+
}, Qe = () => {
|
876
|
+
ie.clearAllStores();
|
877
|
+
};
|
878
|
+
function ae(f, s) {
|
879
|
+
return a.get(f, (s == null ? void 0 : s.idProperty) || "_id");
|
880
|
+
}
|
881
|
+
const Ue = {
|
882
|
+
idProperty: "_id"
|
883
|
+
}, Z = {
|
884
|
+
resource: null,
|
885
|
+
status: "NotLoaded"
|
886
|
+
}, xe = (f, s) => {
|
887
|
+
s = a.merge({}, Ue, s);
|
888
|
+
const R = ye(f, {
|
889
|
+
state: () => ({
|
890
|
+
idProperty: s.idProperty,
|
891
|
+
resourceId: s.resourceId || null,
|
892
|
+
resourceUrl: s.resourceUrl || null,
|
893
|
+
referencePathsToExpand: s.referencePathsToExpand || null,
|
894
|
+
...Z
|
895
|
+
}),
|
896
|
+
getters: {
|
897
|
+
currentResourceUrl: (e) => e.resourceUrl || (s.restCollectionUrl && e.resourceId ? `${s.restCollectionUrl}/${e.resourceId}` : null) || s.restCollectionUrl || null,
|
898
|
+
/** Get the REST query parameters that can be determined from the options. */
|
899
|
+
fixedQueryParams: (e) => {
|
900
|
+
const r = {};
|
901
|
+
return e.referencePathsToExpand && (r.r = V(e.referencePathsToExpand)), r;
|
902
|
+
}
|
903
|
+
},
|
904
|
+
actions: {
|
905
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
906
|
+
// Actions: Configuration
|
907
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
908
|
+
/**
|
909
|
+
* Reset the store to its initial state.
|
910
|
+
*
|
911
|
+
* Configuration is retained, but REST resource content and contextual state are reset.
|
912
|
+
*/
|
913
|
+
reset() {
|
914
|
+
for (const e in Z)
|
915
|
+
this[e] = Z[e];
|
916
|
+
},
|
917
|
+
async setResourceId(e) {
|
918
|
+
const r = this.currentResourceUrl;
|
919
|
+
this.resourceId = e, !a.isEqual(this.currentResourceUrl, r) && this.status != "NotLoaded" && await this.loadResource();
|
920
|
+
},
|
921
|
+
async setResourceUrl(e) {
|
922
|
+
const r = this.currentResourceUrl;
|
923
|
+
this.resourceUrl = e, !a.isEqual(this.currentResourceUrl, r) && this.status != "NotLoaded" && await this.loadResource();
|
924
|
+
},
|
925
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
926
|
+
// Actions: Loading and unloading the collection
|
927
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
928
|
+
/**
|
929
|
+
* Clear the resource.
|
930
|
+
*
|
931
|
+
* Configuration and contextual state are retained, but REST collection contents are reset.
|
932
|
+
*/
|
933
|
+
clear() {
|
934
|
+
for (const e in Z)
|
935
|
+
this[e] = Z[e];
|
936
|
+
},
|
937
|
+
/**
|
938
|
+
* Load the resource, if it has not been loaded.
|
939
|
+
*
|
940
|
+
* The resource will be loaded if its current status is not Loaded, Loading, or Failed.
|
941
|
+
*/
|
942
|
+
async ensureResourceLoaded() {
|
943
|
+
["Loaded", "Loading", "Failed"].includes(this.status) || await this.loadResource();
|
944
|
+
},
|
945
|
+
/**
|
946
|
+
* Load the resource.
|
947
|
+
*
|
948
|
+
* @return A promise that resolves when the resource has been loaded.
|
949
|
+
*/
|
950
|
+
async loadResource() {
|
951
|
+
this.reset();
|
952
|
+
const e = X();
|
953
|
+
if (this._loadId = e, this.currentResourceUrl) {
|
954
|
+
this.status = "Loading";
|
955
|
+
const r = this.fixedQueryParams, t = a.cloneDeep(r), l = a.map(
|
956
|
+
t,
|
957
|
+
(c, d) => c != null ? `${encodeURIComponent(d)}=${encodeURIComponent(c)}` : null
|
958
|
+
).filter((c) => c != null).join("&"), u = a.isEmpty(l) ? this.currentResourceUrl : `${this.currentResourceUrl}?${l}`;
|
959
|
+
try {
|
960
|
+
const c = await P.get(u), d = this.fixedQueryParams;
|
961
|
+
e != this._loadId || !a.isEqual(r, d) ? console.log(`Discarding resource fetched by obsolete query from ${u}.`) : (this.resource = me.decode(a.get(c, "data", {})), this.status = "Loaded");
|
962
|
+
} catch (c) {
|
963
|
+
console.log(`Error while loading resource (URL="${u}")`, c), this.reset(), this.status = "Failed";
|
964
|
+
}
|
965
|
+
} else
|
966
|
+
throw "Cannot load a resource because its URL, or its ID and collection URL, are unknown.";
|
967
|
+
},
|
968
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
969
|
+
// Actions: Recording modifications to the collection
|
970
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
971
|
+
/**
|
972
|
+
* Verify that the resource does not exist in the remote collection.
|
973
|
+
*
|
974
|
+
* After checking the remote collection and verifying that the resource does not exist, this function calls
|
975
|
+
* {@link recordDeletion} to delete it from the local collection.
|
976
|
+
*
|
977
|
+
* @return true if the record does not exist in the remote collection, false if it does or if an error occurred.
|
978
|
+
*/
|
979
|
+
async checkForDeletedResource() {
|
980
|
+
if (this.currentResourceUrl) {
|
981
|
+
let e;
|
982
|
+
try {
|
983
|
+
e = await P.get(this.currentResourceUrl);
|
984
|
+
} catch (r) {
|
985
|
+
P.isAxiosError(r) && (e = r.response);
|
986
|
+
}
|
987
|
+
return e && e.status == 404 ? (this.recordDeletion(), !0) : !1;
|
988
|
+
} else
|
989
|
+
throw "Cannot check for a deleted resource because its URL, or its ID and collection URL, are unknown.";
|
990
|
+
},
|
991
|
+
/**
|
992
|
+
* Record a deletion, removing the deleted resource from the local collection.
|
993
|
+
*
|
994
|
+
* This should be called after deleting the resource from the remote collection. It is called, for instance, by
|
995
|
+
* {@link deleteResource}; but it can also be called when the application is aware of a deletion from the remote
|
996
|
+
* collection made by other means.
|
997
|
+
*/
|
998
|
+
recordDeletion() {
|
999
|
+
this.resource = null, this.status = "Loaded";
|
1000
|
+
},
|
1001
|
+
/**
|
1002
|
+
* Record an insertion, adding the inserted resource to the local collection.
|
1003
|
+
*
|
1004
|
+
* This should be called after inserting a resource into the remote collection. It is called, for instance, by
|
1005
|
+
* {@link saveResource}; but it can also be called when the application is aware of insertions into the remote
|
1006
|
+
* collection made by other means.
|
1007
|
+
*
|
1008
|
+
* This should only be called if the inserted resource is a saved version of the resource tracked by this store.
|
1009
|
+
* Otherwise the store's resource may not match its resourceId and resourceUrl properties when the function
|
1010
|
+
* returns.
|
1011
|
+
*
|
1012
|
+
* It is important that the resources to be added to the local collection have the same object-graph
|
1013
|
+
* characteristics as other resources in the local collection. (These characteristics include property inclusions
|
1014
|
+
* and exclusions, as well as reference expansions.) To ensure this, you may call {@refreshResource} to fetch the
|
1015
|
+
* resource using the collection's settings.
|
1016
|
+
*
|
1017
|
+
* @param resource The resource that has been inserted.
|
1018
|
+
*/
|
1019
|
+
recordInsertion(e) {
|
1020
|
+
this.resourceId = ae(e, this) || null, this.status == "Loaded" && (this.resource = Object.freeze(e));
|
1021
|
+
},
|
1022
|
+
/**
|
1023
|
+
* Record an update, making the same changes in the local resource.
|
1024
|
+
*
|
1025
|
+
* This should be called after updating a remote resource. It is called, for instance, by {@link saveResource} and
|
1026
|
+
* {@link refreshResource}; but it can also be called when the application is aware of updates in the remote
|
1027
|
+
* collection made by other means.
|
1028
|
+
*
|
1029
|
+
* This should only be called if the inserted resource is a saved version of the resource tracked by this store.
|
1030
|
+
* Otherwise the store's resource may not match its resourceId and resourceUrl properties when the function
|
1031
|
+
* returns.
|
1032
|
+
*
|
1033
|
+
* It is important that the resource have the same object-graph characteristics as other resources in the local
|
1034
|
+
* collection. (These characteristics include property inclusions and exclusions, as well as reference
|
1035
|
+
* expansions.) To ensure this, you may instead call {@refreshResource} to fetch the resource using the
|
1036
|
+
* collection's settings; this will in turn call {@link recordUpdate}.
|
1037
|
+
*
|
1038
|
+
* @param resource The resource that has been updated.
|
1039
|
+
*/
|
1040
|
+
recordUpdate(e) {
|
1041
|
+
this.resourceId = ae(e, this) || null, this.status == "Loaded" && (this.resource = Object.freeze(e));
|
1042
|
+
},
|
1043
|
+
async refreshResource() {
|
1044
|
+
return await this.loadResource(), this.resource;
|
1045
|
+
},
|
1046
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
1047
|
+
// Actions: Modifying the resource
|
1048
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
1049
|
+
/**
|
1050
|
+
* Delete the resource.
|
1051
|
+
*
|
1052
|
+
* A delete request is sent to the REST API. If it succeeds, the resource is removed from the local store.
|
1053
|
+
*/
|
1054
|
+
async deleteResource() {
|
1055
|
+
if (this.currentResourceUrl)
|
1056
|
+
(await P.delete(this.currentResourceUrl)).status == 200 && this.recordDeletion();
|
1057
|
+
else
|
1058
|
+
throw "Cannot delete a resource because its URL, or its ID and collection URL, are unknown.";
|
1059
|
+
},
|
1060
|
+
/**
|
1061
|
+
* Write a new or updated resource to the remote resource, then mirror the change locally.
|
1062
|
+
*
|
1063
|
+
* After writing changes, {@link refreshResource} is called to ensure that the local resource (which is also the
|
1064
|
+
* return value) has the same object-graph characteristics as the rest of the local collection.
|
1065
|
+
*
|
1066
|
+
* @param resource The new or updated resource. If it has an ID, an update will be attempted; otherwise it will be
|
1067
|
+
* created.
|
1068
|
+
* @returns The refreshed local resource after the change has been persisted to the remote collection, or null if
|
1069
|
+
* the operation failed.
|
1070
|
+
*/
|
1071
|
+
async saveResource(e) {
|
1072
|
+
const r = ae(e, this), t = r == null;
|
1073
|
+
if (!r)
|
1074
|
+
throw "The REST resource store cannot create new resources on the server.";
|
1075
|
+
if (!this.currentResourceUrl)
|
1076
|
+
throw "Cannot save a resource because its URL, or its ID and collection URL, are unknown.";
|
1077
|
+
const l = await P({
|
1078
|
+
method: t ? "post" : "put",
|
1079
|
+
url: this.currentResourceUrl,
|
1080
|
+
data: e
|
1081
|
+
});
|
1082
|
+
if (l.status == 200) {
|
1083
|
+
const u = l.data, c = await this.refreshResource();
|
1084
|
+
return t && this.recordInsertion(c || u), c || Object.freeze(u);
|
1085
|
+
}
|
1086
|
+
return null;
|
1087
|
+
},
|
1088
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
1089
|
+
// Actions: Custom API endpoints
|
1090
|
+
// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
1091
|
+
async makeCustomApiRequest(e) {
|
1092
|
+
const { method: r, subpath: t, url: l, data: u } = e;
|
1093
|
+
let c = l;
|
1094
|
+
if (!c)
|
1095
|
+
if (this.currentResourceUrl)
|
1096
|
+
c = `${this.currentResourceUrl}/${t}`;
|
1097
|
+
else
|
1098
|
+
throw "Cannot make a custom API request for a resource because its URL, or its ID and collection URL, are unknown.";
|
1099
|
+
const d = await P({
|
1100
|
+
method: r || "get",
|
1101
|
+
url: c,
|
1102
|
+
data: u
|
1103
|
+
});
|
1104
|
+
return d.status >= 200 && d.status < 300 ? d.data : null;
|
1105
|
+
}
|
1106
|
+
}
|
1107
|
+
});
|
1108
|
+
return (() => {
|
1109
|
+
const e = (r, t) => {
|
1110
|
+
const l = R(r, t);
|
1111
|
+
return ne.registerStore(l.$id, l), l;
|
1112
|
+
};
|
1113
|
+
return e.$id = R.$id, e;
|
1114
|
+
})();
|
1115
|
+
}, ne = new Ie(), Me = (f = {}) => {
|
1116
|
+
const {
|
1117
|
+
resourceClientId: s,
|
1118
|
+
draftBatchId: R,
|
1119
|
+
enabled: L,
|
1120
|
+
resourceType: e,
|
1121
|
+
options: r
|
1122
|
+
} = f, t = I(void 0), l = I(s), u = I(R), c = I(L ?? !0), d = I([te(e)]), y = I(r), U = m(() => u.value), T = I(c.value), b = I(X()), q = m(
|
1123
|
+
() => u.value == null && l.value != null && ne.getStore(l.value) != null
|
1124
|
+
), A = I(q.value), z = m(() => S("status", "Uninitialized"));
|
1125
|
+
function B(n) {
|
1126
|
+
if (t.value)
|
1127
|
+
return n(t.value);
|
1128
|
+
}
|
1129
|
+
async function g(n) {
|
1130
|
+
if (t.value)
|
1131
|
+
return await n(t.value);
|
1132
|
+
}
|
1133
|
+
function S(n, h) {
|
1134
|
+
if (t.value)
|
1135
|
+
return t.value[n];
|
1136
|
+
if (h !== void 0)
|
1137
|
+
return h;
|
1138
|
+
throw "Attempted to use a REST resource store that has not been created, with no default value.";
|
1139
|
+
}
|
1140
|
+
const E = (n = {}) => {
|
1141
|
+
const {
|
1142
|
+
resourceClientId: h,
|
1143
|
+
draftBatchId: F,
|
1144
|
+
enabled: N,
|
1145
|
+
resourceType: G,
|
1146
|
+
options: j
|
1147
|
+
} = n;
|
1148
|
+
l.value = h, u.value = F, c.value = N ?? !0, d.value = [te(G)], j != null && (y.value = j), b.value = X(), $(c.value);
|
1149
|
+
}, $ = async (n) => {
|
1150
|
+
n != T.value && (T.value = n, n && x());
|
1151
|
+
}, D = m(
|
1152
|
+
() => d.value[0].value || void 0
|
1153
|
+
), W = function(n) {
|
1154
|
+
const h = a.get(n, "referencePathsToExpand"), F = D.value ? a.isFunction(h) ? h(D.value) : h || [] : [];
|
1155
|
+
return {
|
1156
|
+
...a.cloneDeep(n || {}),
|
1157
|
+
referencePathsToExpand: F
|
1158
|
+
};
|
1159
|
+
}, H = m(() => W(y.value)), J = m(() => {
|
1160
|
+
if (!D.value)
|
1161
|
+
return null;
|
1162
|
+
if (!A.value)
|
1163
|
+
return !D.value || !D.value.collectionName ? null : U.value ? `${ee.config.apiBaseUrl}/draft-batches/${U.value}/${D.value.collectionName}` : `${ee.config.apiBaseUrl}/${D.value.collectionName}`;
|
1164
|
+
}), K = m(() => {
|
1165
|
+
if (D.value && J.value)
|
1166
|
+
return a.mergeWith(
|
1167
|
+
a.cloneDeep(Ue),
|
1168
|
+
H.value,
|
1169
|
+
{
|
1170
|
+
idProperty: D.value.idProperty,
|
1171
|
+
restCollectionUrl: J.value
|
1172
|
+
},
|
1173
|
+
(n, h) => {
|
1174
|
+
if (a.isArray(h) && h.length == 0 && (n == null || a.isArray(n)))
|
1175
|
+
return h;
|
1176
|
+
}
|
1177
|
+
);
|
1178
|
+
}), p = I(0), k = m(
|
1179
|
+
() => {
|
1180
|
+
var n;
|
1181
|
+
return p.value, l.value || D.value && ((n = D.value) == null ? void 0 : n.collectionName) && (U.value ? `draftBatches/${U.value}/${D.value.collectionName}/${b.value}` : `${D.value.collectionName}/${b.value}`) || null;
|
1182
|
+
}
|
1183
|
+
), x = () => {
|
1184
|
+
if (T.value) {
|
1185
|
+
let n = !1;
|
1186
|
+
return D.value && k.value && (t.value = ne.getStore(k.value), t.value ? n = !1 : K.value && (A.value = !1, t.value = xe(k.value, K.value)(), n = !0)), n;
|
1187
|
+
} else
|
1188
|
+
return !1;
|
1189
|
+
}, M = () => {
|
1190
|
+
k.value && l.value == k.value ? (t.value = void 0, x()) : (t.value = void 0, p.value = p.value + 1, x());
|
1191
|
+
};
|
1192
|
+
return C(U, (n, h) => {
|
1193
|
+
a.isEqual(n, h) || M();
|
1194
|
+
}), C(T, (n, h) => {
|
1195
|
+
n != h && (n ? x() : t.value = void 0);
|
1196
|
+
}), C(D, (n, h) => {
|
1197
|
+
n && !h ? x() : !n && h || a.isEqual(n, h) || M();
|
1198
|
+
}), C(K, (n, h) => {
|
1199
|
+
a.isEqual(n, h) || M();
|
1200
|
+
}), C(q, () => {
|
1201
|
+
q.value || (A.value = !1);
|
1202
|
+
}), C(k, () => x()), C(J, (n, h) => {
|
1203
|
+
a.isEqual(n, h) || M();
|
1204
|
+
}), x(), {
|
1205
|
+
// Configuration
|
1206
|
+
resourceClientId: k,
|
1207
|
+
draftBatchId: U,
|
1208
|
+
enabled: T,
|
1209
|
+
resourceType: D,
|
1210
|
+
// Status
|
1211
|
+
status: z,
|
1212
|
+
// Resources loaded from the REST API
|
1213
|
+
resource: m(() => z.value != "Uninitialized" ? (g((n) => n.ensureResourceLoaded()), S("resource", [])) : []),
|
1214
|
+
resourceId: m(() => S("resourceId", null)),
|
1215
|
+
resourceUrl: m(() => S("resourceUrl", null)),
|
1216
|
+
// Methods: Configuration
|
1217
|
+
reconfigureClient: E,
|
1218
|
+
setEnabled: $,
|
1219
|
+
setResourceId: async (n) => await g(async (h) => await h.setResourceId(n)),
|
1220
|
+
setResourceUrl: async (n) => await g(async (h) => await h.setResourceUrl(n)),
|
1221
|
+
// Methods: Status
|
1222
|
+
ensureStore: x,
|
1223
|
+
// Methods: Resource management
|
1224
|
+
clear: () => B((n) => n.clear()),
|
1225
|
+
ensureResourceLoaded: async () => await g(async (n) => n.ensureResourceLoaded()),
|
1226
|
+
loadResource: async () => await g(async (n) => await n.loadResource()),
|
1227
|
+
// Methods: Recording changes to the resource
|
1228
|
+
checkForDeletedResource: async () => await g(async (n) => await n.checkForDeletedResource()) == !0,
|
1229
|
+
recordDeletion: () => g((n) => n.recordDeletion()),
|
1230
|
+
recordInsertion: (n) => g((h) => h.recordInsertion(n)),
|
1231
|
+
recordUpdate: (n) => g((h) => h.recordUpdate(n)),
|
1232
|
+
refreshResource: async () => await g(async (n) => await n.refreshResource()),
|
1233
|
+
// Methods: Modifying the resource
|
1234
|
+
deleteResource: async () => await g(async (n) => await n.deleteResource()),
|
1235
|
+
saveResource: async (n) => await g(async (h) => await h.saveResource(n)),
|
1236
|
+
// Methods: Custom API endpoints
|
1237
|
+
makeCustomApiRequest: async (n) => await g(
|
1238
|
+
async (h) => await h.makeCustomApiRequest(n)
|
1239
|
+
)
|
1240
|
+
};
|
1241
|
+
}, ee = {
|
1242
|
+
config: { ...pe }
|
1243
|
+
};
|
1244
|
+
function je(f) {
|
1245
|
+
ee.config = a.merge({}, pe, f);
|
1246
|
+
}
|
1247
|
+
export {
|
1248
|
+
ke as ApiClientError,
|
1249
|
+
Qe as clearAllRestCollections,
|
1250
|
+
je as initRestClient,
|
1251
|
+
ee as restClient,
|
1252
|
+
Be as useRestCollection,
|
1253
|
+
Me as useRestResource
|
1254
|
+
};
|