@larc-iu/plaid-client 0.2.0-alpha.13 → 0.2.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +212 -14
- package/package.json +1 -1
- package/src/documentLock.js +299 -0
- package/src/http.js +143 -53
- package/src/index.js +387 -151
- package/src/pagination.js +19 -35
- package/src/serviceSchema.js +27 -1
- package/src/services.js +137 -21
package/src/http.js
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
import { transformRequest, transformResponse } from "./transforms.js";
|
|
2
2
|
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Batches: which calls a batch carries, and which go over the wire anyway.
|
|
5
|
+
//
|
|
6
|
+
// A batch is an OBJECT (`client.batch()`, or the argument `client.batched(fn)`
|
|
7
|
+
// hands `fn`), a view of the client with the same bundles. A write made on the
|
|
8
|
+
// batch queues; a call made on the client itself always goes over the wire,
|
|
9
|
+
// whatever batches happen to be open. So the editor, the importer and the
|
|
10
|
+
// app's chrome can share one client, and a write by code that knows nothing
|
|
11
|
+
// about a batch can never land inside it. Every call reaching this layer is
|
|
12
|
+
// one of three things, and the third column is what a new endpoint has to
|
|
13
|
+
// pick.
|
|
14
|
+
//
|
|
15
|
+
// 1. A WRITE of project data: an entity, its metadata, a layer, a membership,
|
|
16
|
+
// a comment. This is what a batch is for, so on a batch it QUEUES. It is
|
|
17
|
+
// the default and needs no flag.
|
|
18
|
+
//
|
|
19
|
+
// 2. A READ. Every GET is answered from the wire even when made on a batch:
|
|
20
|
+
// the caller gets data rather than `{ batched: true }`, it takes no slot in
|
|
21
|
+
// the batch's results, and server-side it runs against the pool rather than
|
|
22
|
+
// the batch's transaction connection. A read that travels as a POST
|
|
23
|
+
// (`query`) says so with `outOfBand`.
|
|
24
|
+
//
|
|
25
|
+
// 3. An OUT-OF-BAND SIGNAL: stopping a service request, reporting a service's
|
|
26
|
+
// progress or result, taking or dropping a document lock, forgetting a
|
|
27
|
+
// service, an admin action on the server itself. It is shaped like a write
|
|
28
|
+
// but carries no project data, and its whole value is that it happens NOW.
|
|
29
|
+
// Pass `outOfBand`: made on a batch it still goes over the wire, and it
|
|
30
|
+
// never joins an open logical operation. The audit group is a label for
|
|
31
|
+
// the writes, and a signal that is never audited would mark the group
|
|
32
|
+
// written and leave the relabel PATCH 404ing on a group nothing ever
|
|
33
|
+
// created.
|
|
34
|
+
//
|
|
35
|
+
// Strict mode stamps the batch's one expected document-version onto the first
|
|
36
|
+
// write queued on it (whole-batch OCC: stamping every op would 409 the second
|
|
37
|
+
// against the bump the first caused). A call that went over the wire on its
|
|
38
|
+
// own carries its own stamp and spends nothing of any batch's.
|
|
39
|
+
//
|
|
40
|
+
// `noBatch` is not part of that judgment. It marks the five calls the batch
|
|
41
|
+
// transport cannot carry at all (a batch inside a batch, the multipart media
|
|
42
|
+
// and avatar uploads, the user-data store's put and delete) and raises so the
|
|
43
|
+
// caller finds out. Never put it on a read: it turns a swallowed read into a
|
|
44
|
+
// thrown one, which is what the chrome hit when an unrelated import was
|
|
45
|
+
// running. A blobless DELETE beside an upload is not one of them: it carries
|
|
46
|
+
// nothing the transport cannot express, so it takes its class from the three
|
|
47
|
+
// above like anything else.
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
3
50
|
// Default per-request timeout (ms). Applied to every request unless the client
|
|
4
51
|
// is constructed with a different `timeout` (0 / null disables it). Note: this
|
|
5
52
|
// also bounds media up/downloads — bump it (or disable) for very large files.
|
|
@@ -253,14 +300,13 @@ export function xhrSend(
|
|
|
253
300
|
* rawBody - Body value passed directly (no transform). Mutually exclusive with body.
|
|
254
301
|
* formData - If true, body is FormData; skip Content-Type header
|
|
255
302
|
* queryParams - Object of query param key/values to append
|
|
256
|
-
* noBatch - If true, throw when
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
* batch (see `query`).
|
|
303
|
+
* noBatch - If true, throw when made on a batch. Only for calls the
|
|
304
|
+
* batch transport cannot carry at all (see the note at the
|
|
305
|
+
* top of this file); never for a read.
|
|
306
|
+
* outOfBand - If true, the call is a signal rather than a write of
|
|
307
|
+
* project data: made on a batch it still goes over the
|
|
308
|
+
* wire, and it never joins an open logical operation (see
|
|
309
|
+
* the note at the top of this file).
|
|
264
310
|
* skipResponseTransform - Return raw parsed JSON (no transformResponse)
|
|
265
311
|
* noAuth - Skip Authorization header
|
|
266
312
|
* binaryResponse - Return arrayBuffer instead of JSON/text
|
|
@@ -273,21 +319,34 @@ export function xhrSend(
|
|
|
273
319
|
* where the timeout only fires when the upload stalls;
|
|
274
320
|
* elsewhere the callback is ignored and fetch is used.
|
|
275
321
|
*/
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
322
|
+
/**
|
|
323
|
+
* Everything a request is before it goes anywhere: the URL with its query
|
|
324
|
+
* params and the stamps strict mode, a per-call audit message and an open
|
|
325
|
+
* logical operation add, plus the transformed body. Shared by the wire path
|
|
326
|
+
* (`makeRequest`) and the batch path (`queueRequest`), so a queued op is
|
|
327
|
+
* exactly the request that would have gone out. `batch` is the batch the call
|
|
328
|
+
* is being queued on, if any: strict mode stamps its document-version onto the
|
|
329
|
+
* first write queued there and no other.
|
|
330
|
+
*/
|
|
331
|
+
export function prepareRequest(
|
|
332
|
+
client,
|
|
333
|
+
method,
|
|
334
|
+
path,
|
|
335
|
+
options = {},
|
|
336
|
+
batch = null,
|
|
337
|
+
) {
|
|
338
|
+
const { body, rawBody, formData, queryParams, outOfBand, auditMessage } =
|
|
339
|
+
options;
|
|
340
|
+
|
|
341
|
+
// A write must not go out on a lock that lapsed. `documents.locked()`
|
|
342
|
+
// records the loss here when its keep-alive cannot renew, and from that
|
|
343
|
+
// moment the block is holding nothing: an edit by somebody else can already
|
|
344
|
+
// have landed between the read the work was planned from and the write about
|
|
345
|
+
// to go out. Reads pass, and so do the lock routes themselves, which is how
|
|
346
|
+
// the block still releases on its way out.
|
|
347
|
+
if (client.documentLockLost && method !== "GET" && !path.endsWith("/lock")) {
|
|
348
|
+
throw client.documentLockLost;
|
|
349
|
+
}
|
|
291
350
|
|
|
292
351
|
// Build URL
|
|
293
352
|
let url = `${client.baseUrl}${path}`;
|
|
@@ -317,23 +376,19 @@ export async function makeRequest(client, method, path, options = {}) {
|
|
|
317
376
|
requestBody = transformRequest(body);
|
|
318
377
|
}
|
|
319
378
|
|
|
320
|
-
// Strict mode: append document-version for non-GET requests
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
method !== "GET" &&
|
|
329
|
-
!(client.isBatching && client.batchVersionStamped)
|
|
330
|
-
) {
|
|
379
|
+
// Strict mode: append document-version for non-GET requests, on EVERY
|
|
380
|
+
// queued write of a batch. The server validates the first write it can
|
|
381
|
+
// (one whose route resolves a document) and skips the rest of that
|
|
382
|
+
// document's, so the version bump a sub-op causes does not 409 the next
|
|
383
|
+
// one. Stamping only the first write was silently no check at all
|
|
384
|
+
// whenever that write was one the route ignores, such as a vocabulary
|
|
385
|
+
// entry's metadata, which is exactly what the igt editor queues first.
|
|
386
|
+
if (client.strictModeDocumentId && method !== "GET") {
|
|
331
387
|
const docId = client.strictModeDocumentId;
|
|
332
388
|
if (client.documentVersions[docId]) {
|
|
333
389
|
const docVersion = client.documentVersions[docId];
|
|
334
390
|
const separator = url.includes("?") ? "&" : "?";
|
|
335
391
|
url += `${separator}document-version=${encodeURIComponent(docVersion)}`;
|
|
336
|
-
if (client.isBatching) client.batchVersionStamped = true;
|
|
337
392
|
}
|
|
338
393
|
}
|
|
339
394
|
|
|
@@ -350,7 +405,12 @@ export async function makeRequest(client, method, path, options = {}) {
|
|
|
350
405
|
// Logical-operation group (see client.beginOperation): stamp every write
|
|
351
406
|
// with the group id; the message rides along too so the server can label
|
|
352
407
|
// the group lazily on whichever tagged write lands first.
|
|
353
|
-
|
|
408
|
+
//
|
|
409
|
+
// An out-of-band signal is not one of those writes (see the note at the top
|
|
410
|
+
// of this file): it never lands in the audit log, so a stamp does nothing
|
|
411
|
+
// server-side while `written` promises a group that will never exist, and
|
|
412
|
+
// the relabel PATCH then 404s.
|
|
413
|
+
if (client.operationGroup && method !== "GET" && !outOfBand) {
|
|
354
414
|
const group = client.operationGroup;
|
|
355
415
|
const separator = url.includes("?") ? "&" : "?";
|
|
356
416
|
url += `${separator}group-id=${encodeURIComponent(group.id)}`;
|
|
@@ -359,24 +419,54 @@ export async function makeRequest(client, method, path, options = {}) {
|
|
|
359
419
|
group.written = true;
|
|
360
420
|
}
|
|
361
421
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
return { batched: true };
|
|
422
|
+
return { url, requestBody };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* A call made on a batch (see `PlaidClient#batch`). A write of project data is
|
|
427
|
+
* queued as one operation of the batch and answers `{ batched: true }`; its
|
|
428
|
+
* result is the matching entry of what `submit()` resolves to. A read, and a
|
|
429
|
+
* signal marked `outOfBand`, is the client's to make and goes over the wire
|
|
430
|
+
* now, exactly as if it had been made on the client.
|
|
431
|
+
*/
|
|
432
|
+
export async function queueRequest(batch, method, path, options = {}) {
|
|
433
|
+
if (method === "GET" || options.outOfBand) {
|
|
434
|
+
return batch.client._request(method, path, options);
|
|
435
|
+
}
|
|
436
|
+
if (!batch.open) {
|
|
437
|
+
throw new Error(`This batch was already submitted or aborted: ${path}`);
|
|
379
438
|
}
|
|
439
|
+
if (options.noBatch) {
|
|
440
|
+
throw new Error(`This endpoint cannot be used in a batch: ${path}`);
|
|
441
|
+
}
|
|
442
|
+
const { url, requestBody } = prepareRequest(
|
|
443
|
+
batch.client,
|
|
444
|
+
method,
|
|
445
|
+
path,
|
|
446
|
+
options,
|
|
447
|
+
batch,
|
|
448
|
+
);
|
|
449
|
+
const operation = {
|
|
450
|
+
path: url.replace(batch.client.baseUrl, ""),
|
|
451
|
+
method: method.toUpperCase(),
|
|
452
|
+
};
|
|
453
|
+
if (requestBody !== undefined) {
|
|
454
|
+
operation.body = requestBody;
|
|
455
|
+
}
|
|
456
|
+
batch.operations.push(operation);
|
|
457
|
+
return { batched: true };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export async function makeRequest(client, method, path, options = {}) {
|
|
461
|
+
const {
|
|
462
|
+
formData,
|
|
463
|
+
skipResponseTransform,
|
|
464
|
+
noAuth,
|
|
465
|
+
binaryResponse,
|
|
466
|
+
timeout,
|
|
467
|
+
onUploadProgress,
|
|
468
|
+
} = options;
|
|
469
|
+
const { url, requestBody } = prepareRequest(client, method, path, options);
|
|
380
470
|
|
|
381
471
|
// Build fetch options
|
|
382
472
|
const headers = {};
|