@ovok/core 0.4.49-dev.497 → 0.4.49-dev.498
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/dist/conformance/capability-requirements.js +1 -1
- package/dist/next/index.d.ts +1 -0
- package/dist/next/ovok.js +7 -1
- package/dist/next/resources/measurements/measurements-resource.d.ts +17 -1
- package/dist/next/resources/measurements/measurements-resource.js +27 -6
- package/dist/next/resources/measurements/offline-measurement-queue-types.d.ts +22 -0
- package/dist/next/resources/measurements/offline-measurement-queue-types.js +2 -0
- package/dist/next/resources/measurements/offline-measurement-queue.d.ts +25 -0
- package/dist/next/resources/measurements/offline-measurement-queue.js +154 -0
- package/package.json +1 -1
|
@@ -48,7 +48,7 @@ exports.OVOK_CORE_REQUIREMENTS = {
|
|
|
48
48
|
description: "The FHIR surface a server must provide for the @ovok/core client library's own methods to function.",
|
|
49
49
|
software: {
|
|
50
50
|
name: "@ovok/core",
|
|
51
|
-
version: "0.4.49-dev.
|
|
51
|
+
version: "0.4.49-dev.498+92fd957",
|
|
52
52
|
},
|
|
53
53
|
fhirVersion: "4.0.1",
|
|
54
54
|
format: ["json"],
|
package/dist/next/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export type { HealthCheck } from "./resources/health-resource";
|
|
|
17
17
|
export type { PatientRegistration, PractitionerRegistration, RegisterResult } from "./resources/auth/registration";
|
|
18
18
|
export type { SocialCredentials, SocialProvider } from "./resources/auth/social-login";
|
|
19
19
|
export type { LastObservationsQuery, LatestObservationsParams, MeasurementCodeTypes, SaveObservationsOptions, SaveObservationsResult, } from "./resources/measurements/measurements-resource";
|
|
20
|
+
export type { OfflineMeasurementFailure, OfflineMeasurementFlushOptions, OfflineMeasurementFlushResult, OfflineMeasurementQueueApi, } from "./resources/measurements/offline-measurement-queue";
|
|
20
21
|
export type { BuildObservationsParams } from "./resources/measurements/observation-builder";
|
|
21
22
|
export type { SignalsDeliveryRepair, SignalsDeliveryRepairResult, SignalsUndeliverable, SignalsUndeliverableResident, } from "./resources/signals/undeliverable-resource";
|
|
22
23
|
export type { ProjectListQuery } from "./resources/projects/projects-resource";
|
package/dist/next/ovok.js
CHANGED
|
@@ -24,6 +24,7 @@ const invites_resource_1 = require("./resources/invites/invites-resource");
|
|
|
24
24
|
const locales_resource_1 = require("./resources/localization/locales-resource");
|
|
25
25
|
const localizations_resource_1 = require("./resources/localization/localizations-resource");
|
|
26
26
|
const measurements_resource_1 = require("./resources/measurements/measurements-resource");
|
|
27
|
+
const offline_measurement_queue_1 = require("./resources/measurements/offline-measurement-queue");
|
|
27
28
|
const patient_resource_1 = require("./resources/patient/patient-resource");
|
|
28
29
|
const projects_resource_1 = require("./resources/projects/projects-resource");
|
|
29
30
|
const questionnaires_resource_1 = require("./resources/questionnaires/questionnaires-resource");
|
|
@@ -36,6 +37,7 @@ const wearables_resource_1 = require("./resources/wearables/wearables-resource")
|
|
|
36
37
|
/** One client per tenant; each namespace mirrors an area of the Ovok API. */
|
|
37
38
|
class Ovok {
|
|
38
39
|
constructor(config) {
|
|
40
|
+
var _a, _b;
|
|
39
41
|
const settings = (0, settings_1.readSettings)(config, (0, settings_1.currentEnvironment)());
|
|
40
42
|
const credentials = credentialsFor(settings);
|
|
41
43
|
const http = new http_client_1.HttpClient({ settings, credentials, retryPolicy: new retry_policy_1.RetryPolicy(settings.maxNetworkRetries) });
|
|
@@ -46,7 +48,11 @@ class Ovok {
|
|
|
46
48
|
this.fhir = fhir;
|
|
47
49
|
this.health = new health_resource_1.HealthResource(http.anonymous());
|
|
48
50
|
this.patient = new patient_resource_1.PatientResource(http, fhir);
|
|
49
|
-
this.measurements = new measurements_resource_1.MeasurementsResource(http, fhir, session
|
|
51
|
+
this.measurements = new measurements_resource_1.MeasurementsResource(http, fhir, session, {
|
|
52
|
+
storage: settings.storage,
|
|
53
|
+
queueKey: (0, offline_measurement_queue_1.offlineMeasurementStorageKey)(settings.tenantCode, settings.baseUrl),
|
|
54
|
+
machineClientId: (_b = (_a = settings.machineClient) === null || _a === void 0 ? void 0 : _a.clientId) !== null && _b !== void 0 ? _b : null,
|
|
55
|
+
});
|
|
50
56
|
this.questionnaires = new questionnaires_resource_1.QuestionnairesResource(http);
|
|
51
57
|
this.questionnaireResponses = new questionnaires_resource_1.QuestionnaireResponsesResource(http, fhir);
|
|
52
58
|
this.consent = new consent_resource_1.ConsentResource(http);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { OfflineMeasurementQueueApi } from "./offline-measurement-queue";
|
|
1
2
|
import type { GetLatestObservationsByCodeParams } from "../../../client/observation/types/GetLatestObservationsByCodeParams";
|
|
3
|
+
import type { OvokStorage } from "../../storage/ovok-storage";
|
|
2
4
|
import type { Bundle, Observation } from "@medplum/fhirtypes";
|
|
3
5
|
import { observationCodesForTypes } from "../../../client/observation/methods/observationsToMeasurements";
|
|
4
6
|
import { HttpClient } from "../../http/http-client";
|
|
@@ -15,6 +17,14 @@ export type SaveObservationsOptions = RequestOptions & {
|
|
|
15
17
|
export type SaveObservationsResult = {
|
|
16
18
|
status: "saved";
|
|
17
19
|
response: Bundle;
|
|
20
|
+
} | {
|
|
21
|
+
status: "queued";
|
|
22
|
+
queueId: string;
|
|
23
|
+
};
|
|
24
|
+
type MeasurementStorage = {
|
|
25
|
+
storage: OvokStorage;
|
|
26
|
+
queueKey: string;
|
|
27
|
+
machineClientId: string | null;
|
|
18
28
|
};
|
|
19
29
|
export type LatestObservationsParams = GetLatestObservationsByCodeParams;
|
|
20
30
|
export type LastObservationsQuery = {
|
|
@@ -26,7 +36,11 @@ export declare class MeasurementsResource {
|
|
|
26
36
|
private readonly http;
|
|
27
37
|
private readonly fhir;
|
|
28
38
|
private readonly account;
|
|
29
|
-
|
|
39
|
+
private readonly offlineMeasurementQueue;
|
|
40
|
+
private readonly machineClientId;
|
|
41
|
+
private readonly session;
|
|
42
|
+
readonly offlineQueue: OfflineMeasurementQueueApi;
|
|
43
|
+
constructor(http: HttpClient, fhir: FhirResource, session: SessionSource | null, storageOptions: MeasurementStorage);
|
|
30
44
|
save(params: BuildObservationsParams, options?: SaveObservationsOptions): Promise<SaveObservationsResult>;
|
|
31
45
|
/** One reading, main observation first, created conditionally so a retry cannot duplicate it. */
|
|
32
46
|
saveObservations(observations: Observation[], options?: SaveObservationsOptions): Promise<SaveObservationsResult>;
|
|
@@ -38,5 +52,7 @@ export declare class MeasurementsResource {
|
|
|
38
52
|
private latestPatientId;
|
|
39
53
|
/** A reading split over several requests stops once another account signs in, so none of it lands elsewhere. */
|
|
40
54
|
private send;
|
|
55
|
+
private currentOwnerKey;
|
|
41
56
|
private requireOwner;
|
|
42
57
|
}
|
|
58
|
+
export {};
|
|
@@ -8,15 +8,22 @@ const query_string_1 = require("../../../client/query-string");
|
|
|
8
8
|
const ovok_errors_1 = require("../../errors/ovok-errors");
|
|
9
9
|
const url_1 = require("../../http/url");
|
|
10
10
|
const observation_builder_1 = require("./observation-builder");
|
|
11
|
+
const offline_measurement_queue_2 = require("./offline-measurement-queue");
|
|
11
12
|
const save_error_1 = require("./save-error");
|
|
12
13
|
const signed_in_account_1 = require("./signed-in-account");
|
|
13
14
|
const LATEST_OBSERVATIONS_PATH = "fhir/Observation/$lastn";
|
|
14
15
|
const LATEST_PER_CODE = 1;
|
|
15
16
|
class MeasurementsResource {
|
|
16
|
-
constructor(http, fhir, session) {
|
|
17
|
+
constructor(http, fhir, session, storageOptions) {
|
|
17
18
|
this.http = http;
|
|
18
19
|
this.fhir = fhir;
|
|
20
|
+
this.session = session;
|
|
21
|
+
this.machineClientId = storageOptions.machineClientId;
|
|
19
22
|
this.account = new signed_in_account_1.SignedInAccount(session);
|
|
23
|
+
this.offlineMeasurementQueue = new offline_measurement_queue_2.OfflineMeasurementQueue(storageOptions.storage, storageOptions.queueKey, () => this.currentOwnerKey(), (bundle, ownerKey, requestOptions) => this.send(bundle, requestOptions, ownerKey));
|
|
24
|
+
this.offlineQueue = {
|
|
25
|
+
flush: (options, requestOptions) => this.offlineMeasurementQueue.flush(options, requestOptions),
|
|
26
|
+
};
|
|
20
27
|
}
|
|
21
28
|
async save(params, options) {
|
|
22
29
|
return this.saveObservations(await this.build(params), options);
|
|
@@ -25,8 +32,16 @@ class MeasurementsResource {
|
|
|
25
32
|
async saveObservations(observations, options = {}) {
|
|
26
33
|
const { identifierSystems, replace, ...requestOptions } = options;
|
|
27
34
|
const entry = (0, observation_bundle_utils_1.linkObservationMembers)(observations).map((observation) => (0, offline_measurement_queue_1.entryForObservation)(observation, { identifierSystems, replace }));
|
|
28
|
-
const
|
|
29
|
-
|
|
35
|
+
const ownerKey = await this.currentOwnerKey();
|
|
36
|
+
if (entry.length === 0 || ownerKey === null) {
|
|
37
|
+
const response = await this.send({ resourceType: "Bundle", type: "transaction", entry }, requestOptions, ownerKey);
|
|
38
|
+
return { status: "saved", response };
|
|
39
|
+
}
|
|
40
|
+
const queued = await this.offlineMeasurementQueue.enqueueAndFlush(entry, ownerKey, requestOptions);
|
|
41
|
+
const response = queued.flush.responses[queued.id];
|
|
42
|
+
return response
|
|
43
|
+
? { status: "saved", response }
|
|
44
|
+
: { status: "queued", queueId: queued.id };
|
|
30
45
|
}
|
|
31
46
|
async build(params) {
|
|
32
47
|
const subject = params.patientId ? `Patient/${params.patientId}` : await this.account.profileReference();
|
|
@@ -55,8 +70,7 @@ class MeasurementsResource {
|
|
|
55
70
|
return params.patientId;
|
|
56
71
|
}
|
|
57
72
|
/** A reading split over several requests stops once another account signs in, so none of it lands elsewhere. */
|
|
58
|
-
async send(bundle, options) {
|
|
59
|
-
const owner = await this.account.key();
|
|
73
|
+
async send(bundle, options, owner) {
|
|
60
74
|
try {
|
|
61
75
|
return await (0, observation_bundle_utils_1.executeObservationBundle)(bundle, async (request) => {
|
|
62
76
|
await this.requireOwner(owner);
|
|
@@ -67,8 +81,15 @@ class MeasurementsResource {
|
|
|
67
81
|
throw (0, save_error_1.isBundle)(error) ? (0, save_error_1.saveErrorFrom)(error) : error;
|
|
68
82
|
}
|
|
69
83
|
}
|
|
84
|
+
async currentOwnerKey() {
|
|
85
|
+
const accountKey = await this.account.key();
|
|
86
|
+
if (accountKey) {
|
|
87
|
+
return `user:${accountKey}`;
|
|
88
|
+
}
|
|
89
|
+
return this.session === null && this.machineClientId ? `machine:${this.machineClientId}` : null;
|
|
90
|
+
}
|
|
70
91
|
async requireOwner(owner) {
|
|
71
|
-
if (owner !== (await this.
|
|
92
|
+
if (owner !== (await this.currentOwnerKey())) {
|
|
72
93
|
throw new ovok_errors_1.OvokConflictError((0, ovok_errors_1.detailsFor)("The signed-in account changed during the save; the rest was not sent."));
|
|
73
94
|
}
|
|
74
95
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RequestOptions } from "../../http/transmit";
|
|
2
|
+
import type { Bundle } from "@medplum/fhirtypes";
|
|
3
|
+
export type OfflineMeasurementFlushOptions = {
|
|
4
|
+
includeExhausted?: boolean;
|
|
5
|
+
resetExhausted?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type OfflineMeasurementFailure = {
|
|
8
|
+
name: string;
|
|
9
|
+
code?: string;
|
|
10
|
+
status?: number;
|
|
11
|
+
};
|
|
12
|
+
export type OfflineMeasurementFlushResult = {
|
|
13
|
+
attempted: number;
|
|
14
|
+
saved: number;
|
|
15
|
+
queued: number;
|
|
16
|
+
exhausted: number;
|
|
17
|
+
responses: Record<string, Bundle>;
|
|
18
|
+
failures: Record<string, OfflineMeasurementFailure>;
|
|
19
|
+
};
|
|
20
|
+
export type OfflineMeasurementQueueApi = {
|
|
21
|
+
flush: (options?: OfflineMeasurementFlushOptions, requestOptions?: RequestOptions) => Promise<OfflineMeasurementFlushResult>;
|
|
22
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { OfflineMeasurementFlushOptions, OfflineMeasurementFlushResult } from "./offline-measurement-queue-types";
|
|
2
|
+
import type { RequestOptions } from "../../http/transmit";
|
|
3
|
+
import type { OvokStorage } from "../../storage/ovok-storage";
|
|
4
|
+
import type { Bundle, BundleEntry, Observation } from "@medplum/fhirtypes";
|
|
5
|
+
export type { OfflineMeasurementFailure, OfflineMeasurementFlushOptions, OfflineMeasurementFlushResult, OfflineMeasurementQueueApi, } from "./offline-measurement-queue-types";
|
|
6
|
+
type MeasurementTransport = (bundle: Bundle, ownerKey: string, requestOptions?: RequestOptions) => Promise<Bundle>;
|
|
7
|
+
type OwnerKey = () => Promise<string | null>;
|
|
8
|
+
export declare const offlineMeasurementStorageKey: (tenantCode: string, baseUrl: string) => string;
|
|
9
|
+
export declare class OfflineMeasurementQueue {
|
|
10
|
+
private readonly storage;
|
|
11
|
+
private readonly storageKey;
|
|
12
|
+
private readonly getOwnerKey;
|
|
13
|
+
private readonly transport;
|
|
14
|
+
private operation;
|
|
15
|
+
constructor(storage: OvokStorage, storageKey: string, getOwnerKey: OwnerKey, transport: MeasurementTransport);
|
|
16
|
+
enqueueAndFlush: (entries: BundleEntry<Observation>[], ownerKey: string, requestOptions?: RequestOptions) => Promise<{
|
|
17
|
+
id: string;
|
|
18
|
+
flush: OfflineMeasurementFlushResult;
|
|
19
|
+
}>;
|
|
20
|
+
flush: (options?: OfflineMeasurementFlushOptions, requestOptions?: RequestOptions) => Promise<OfflineMeasurementFlushResult>;
|
|
21
|
+
private runExclusive;
|
|
22
|
+
private readQueue;
|
|
23
|
+
private writeQueue;
|
|
24
|
+
private flushQueue;
|
|
25
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OfflineMeasurementQueue = exports.offlineMeasurementStorageKey = void 0;
|
|
4
|
+
const ovok_errors_1 = require("../../errors/ovok-errors");
|
|
5
|
+
const MAX_RETRIES = 8;
|
|
6
|
+
const RETRY_BACKOFF_MS = 5000;
|
|
7
|
+
const MAX_RETRY_BACKOFF_MS = 60 * 60 * 1000;
|
|
8
|
+
const offlineMeasurementStorageKey = (tenantCode, baseUrl) => {
|
|
9
|
+
const base = new URL(baseUrl);
|
|
10
|
+
const path = base.pathname.replace(/\/+$/g, "");
|
|
11
|
+
return `ovok:${tenantCode}:${base.origin}${path}:offline-measurements`;
|
|
12
|
+
};
|
|
13
|
+
exports.offlineMeasurementStorageKey = offlineMeasurementStorageKey;
|
|
14
|
+
const queueItemId = (entries) => entries.map(({ fullUrl }) => fullUrl !== null && fullUrl !== void 0 ? fullUrl : "").join("|");
|
|
15
|
+
const retryable = (error) => error instanceof ovok_errors_1.OvokConnectionError ||
|
|
16
|
+
error instanceof ovok_errors_1.OvokRateLimitError ||
|
|
17
|
+
error instanceof ovok_errors_1.OvokServiceError;
|
|
18
|
+
const summarizeFailure = (error) => {
|
|
19
|
+
const failure = error;
|
|
20
|
+
return {
|
|
21
|
+
name: typeof (failure === null || failure === void 0 ? void 0 : failure.name) === "string" ? failure.name : "Error",
|
|
22
|
+
...(typeof (failure === null || failure === void 0 ? void 0 : failure.code) === "string" ? { code: failure.code } : {}),
|
|
23
|
+
...(typeof (failure === null || failure === void 0 ? void 0 : failure.status) === "number" ? { status: failure.status } : {}),
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
class OfflineMeasurementQueue {
|
|
27
|
+
constructor(storage, storageKey, getOwnerKey, transport) {
|
|
28
|
+
this.storage = storage;
|
|
29
|
+
this.storageKey = storageKey;
|
|
30
|
+
this.getOwnerKey = getOwnerKey;
|
|
31
|
+
this.transport = transport;
|
|
32
|
+
this.operation = Promise.resolve();
|
|
33
|
+
this.enqueueAndFlush = (entries, ownerKey, requestOptions) => this.runExclusive(async () => {
|
|
34
|
+
const queue = await this.readQueue();
|
|
35
|
+
const id = queueItemId(entries);
|
|
36
|
+
const exists = queue.some((item) => item.id === id && item.ownerKey === ownerKey);
|
|
37
|
+
if (!exists) {
|
|
38
|
+
queue.push({
|
|
39
|
+
id,
|
|
40
|
+
ownerKey,
|
|
41
|
+
queuedAt: new Date().toISOString(),
|
|
42
|
+
attempt: 0,
|
|
43
|
+
nextAttemptAt: 0,
|
|
44
|
+
entries,
|
|
45
|
+
});
|
|
46
|
+
await this.writeQueue(queue);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
id,
|
|
50
|
+
flush: await this.flushQueue(queue, {}, requestOptions, true),
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
this.flush = (options = {}, requestOptions) => this.runExclusive(async () => this.flushQueue(await this.readQueue(), options, requestOptions, false));
|
|
54
|
+
this.runExclusive = (operation) => {
|
|
55
|
+
const next = this.operation.then(operation, operation);
|
|
56
|
+
this.operation = next.catch(() => undefined);
|
|
57
|
+
return next;
|
|
58
|
+
};
|
|
59
|
+
this.readQueue = async () => {
|
|
60
|
+
const serialized = await this.storage.getItem(this.storageKey);
|
|
61
|
+
if (serialized === null || serialized === "") {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const queue = JSON.parse(serialized);
|
|
65
|
+
if (!Array.isArray(queue)) {
|
|
66
|
+
throw new Error("Offline measurement queue is not an array.");
|
|
67
|
+
}
|
|
68
|
+
return queue;
|
|
69
|
+
};
|
|
70
|
+
this.writeQueue = async (queue) => {
|
|
71
|
+
if (queue.length === 0) {
|
|
72
|
+
await this.storage.removeItem(this.storageKey);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const serialized = JSON.stringify(queue);
|
|
76
|
+
await this.storage.setItem(this.storageKey, serialized);
|
|
77
|
+
if ((await this.storage.getItem(this.storageKey)) !== serialized) {
|
|
78
|
+
throw new Error("Offline measurement queue could not be persisted.");
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
this.flushQueue = async (queue, options, requestOptions, throwNonRetryable) => {
|
|
82
|
+
const result = {
|
|
83
|
+
attempted: 0,
|
|
84
|
+
saved: 0,
|
|
85
|
+
queued: 0,
|
|
86
|
+
exhausted: 0,
|
|
87
|
+
responses: {},
|
|
88
|
+
failures: {},
|
|
89
|
+
};
|
|
90
|
+
let changed = false;
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
for (const item of [...queue]) {
|
|
93
|
+
const ownerKey = await this.getOwnerKey();
|
|
94
|
+
if (!ownerKey || item.ownerKey !== ownerKey) {
|
|
95
|
+
result.queued++;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (item.lastFailure) {
|
|
99
|
+
result.failures[item.id] = item.lastFailure;
|
|
100
|
+
}
|
|
101
|
+
if (item.exhausted && options.resetExhausted) {
|
|
102
|
+
item.attempt = 0;
|
|
103
|
+
item.exhausted = false;
|
|
104
|
+
item.nextAttemptAt = 0;
|
|
105
|
+
changed = true;
|
|
106
|
+
}
|
|
107
|
+
if ((item.exhausted && !options.includeExhausted) || item.nextAttemptAt > now) {
|
|
108
|
+
if (item.exhausted) {
|
|
109
|
+
result.exhausted++;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
result.queued++;
|
|
113
|
+
}
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
result.attempted++;
|
|
117
|
+
try {
|
|
118
|
+
const response = await this.transport({ resourceType: "Bundle", type: "transaction", entry: item.entries }, item.ownerKey, requestOptions);
|
|
119
|
+
queue.splice(queue.indexOf(item), 1);
|
|
120
|
+
result.saved++;
|
|
121
|
+
result.responses[item.id] = response;
|
|
122
|
+
delete result.failures[item.id];
|
|
123
|
+
changed = true;
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
if (throwNonRetryable && !retryable(error)) {
|
|
127
|
+
queue.splice(queue.indexOf(item), 1);
|
|
128
|
+
await this.writeQueue(queue);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
item.attempt++;
|
|
132
|
+
item.exhausted = item.attempt >= MAX_RETRIES;
|
|
133
|
+
item.nextAttemptAt = item.exhausted
|
|
134
|
+
? Number.MAX_SAFE_INTEGER
|
|
135
|
+
: Date.now() + Math.min(MAX_RETRY_BACKOFF_MS, RETRY_BACKOFF_MS * 2 ** (item.attempt - 1));
|
|
136
|
+
item.lastFailure = summarizeFailure(error);
|
|
137
|
+
result.failures[item.id] = item.lastFailure;
|
|
138
|
+
if (item.exhausted) {
|
|
139
|
+
result.exhausted++;
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
result.queued++;
|
|
143
|
+
}
|
|
144
|
+
changed = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (changed) {
|
|
148
|
+
await this.writeQueue(queue);
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.OfflineMeasurementQueue = OfflineMeasurementQueue;
|