UNPKG

semantic-network

Version:

A utility library for manipulating a list of links that form a semantic interface to a network of resources.

649 lines 40.4 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __values = (this && this.__values) || function(o) { var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; if (m) return m.call(o); if (o && typeof o.length === "number") return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TrackedRepresentationFactory = void 0; var semantic_link_1 = require("semantic-link"); var status_1 = require("./status"); var singletonMerger_1 = require("./singletonMerger"); var linkRelation_1 = require("../linkRelation"); var httpRequestFactory_1 = require("../http/httpRequestFactory"); var trackedRepresentationUtil_1 = require("../utils/trackedRepresentationUtil"); var httpRequestError_1 = require("../interfaces/httpRequestError"); var promiseWaitAll_1 = require("../utils/promiseWaitAll"); var collectionMerger_1 = require("./collectionMerger"); var sparseRepresentationFactory_1 = require("./sparseRepresentationFactory"); var anylogger_1 = __importDefault(require("anylogger")); var representationUtil_1 = require("../utils/representationUtil"); var instanceOfTrackedRepresentation_1 = require("../utils/instanceOf/instanceOfTrackedRepresentation"); var instanceOfFeed_1 = require("../utils/instanceOf/instanceOfFeed"); var instanceOfCollection_1 = require("../utils/instanceOf/instanceOfCollection"); var defaultRequestOptions_1 = require("../http/defaultRequestOptions"); var requestHeaders_1 = require("./requestHeaders"); var cloneDetached_1 = require("./cloneDetached"); var log = (0, anylogger_1.default)('TrackedRepresentationFactory'); var TrackedRepresentationFactory = exports.TrackedRepresentationFactory = /** @class */ (function () { function TrackedRepresentationFactory() { } /** * Creates (POST) a representation in the context of a resource. The resulting representation from the Location header * is hydrated and returned. * * Note: a 201 returns a location whereas the 200 and 202 do not and undef * * @param resource context in which a resource is created * @param document content of the representation * @param options * @returns a 201 returns a representation whereas the 200 and 202 return undefined * @throws AxiosError */ TrackedRepresentationFactory.create = function (resource, document, options) { return __awaiter(this, void 0, void 0, function () { var rel, getUri, throwOnCreateError, uri, response, headers, status_2, uri_1, hydrated, e_1; var _a, _b, _c, _d, _e; return __generator(this, function (_f) { switch (_f.label) { case 0: rel = (_a = __assign({}, options), _b = _a.rel, _b === void 0 ? linkRelation_1.LinkRelation.Self : _b), getUri = (_c = _a.getUri, _c === void 0 ? semantic_link_1.LinkUtil.getUri : _c), throwOnCreateError = (_d = _a.throwOnCreateError, _d === void 0 ? defaultRequestOptions_1.defaultRequestOptions.throwOnCreateError : _d); uri = getUri(resource, rel); log.debug('tracked representation create: start'); if (!uri) return [3 /*break*/, 12]; _f.label = 1; case 1: _f.trys.push([1, 10, , 11]); return [4 /*yield*/, httpRequestFactory_1.HttpRequestFactory.Instance().create(resource, document, options)]; case 2: response = _f.sent(); if (!response) return [3 /*break*/, 8]; headers = (_e = response.headers, _e === void 0 ? {} : _e), status_2 = response.status; if (!headers) { // realistically only for tests log.error('response does not like an http request'); } if (!(status_2 === 201 || !status_2)) return [3 /*break*/, 6]; if (!status_2) { // cater for tests not return status headers log.warn('server not returning status code'); } uri_1 = headers.location; if (!uri_1) return [3 /*break*/, 4]; return [4 /*yield*/, this.load(sparseRepresentationFactory_1.SparseRepresentationFactory.make(__assign(__assign({}, options), { uri: uri_1 })), __assign(__assign({}, options), { rel: linkRelation_1.LinkRelation.Self }))]; case 3: hydrated = _f.sent(); log.debug('tracked representation created and loaded %s', uri_1); return [2 /*return*/, hydrated]; case 4: log.error('create: response no Location header for \'%s\'', uri_1); _f.label = 5; case 5: return [3 /*break*/, 7]; case 6: // other response codes (200, 202) should be dealt with separately // see https://stackoverflow.com/a/29096228 log.warn('response returned %s, no resource processed', status_2); _f.label = 7; case 7: return [3 /*break*/, 9]; case 8: log.error('response not found on http request'); _f.label = 9; case 9: return [3 /*break*/, 11]; case 10: e_1 = _f.sent(); if ((0, httpRequestError_1.isHttpRequestError)(e_1)) { // errors don't get attached back on the context resource, just log them log.warn("Request error returning undefined: '".concat(e_1.message, "'}")); // fall through to undefined } if (throwOnCreateError) { throw e_1; } return [3 /*break*/, 11]; case 11: return [3 /*break*/, 13]; case 12: return [2 /*return*/, Promise.reject(new Error('create tracked representation has no context to find uri to POST on'))]; case 13: return [2 /*return*/, undefined]; } }); }); }; TrackedRepresentationFactory.del = function (resource, options) { return __awaiter(this, void 0, void 0, function () { var rel, getUri, uri, trackedState, response, e_2; var _a, _b, _c; return __generator(this, function (_d) { switch (_d.label) { case 0: if (!(0, instanceOfTrackedRepresentation_1.instanceOfTrackedRepresentation)(resource)) return [3 /*break*/, 7]; rel = (_a = __assign({}, options), _b = _a.rel, _b === void 0 ? linkRelation_1.LinkRelation.Self : _b), getUri = (_c = _a.getUri, _c === void 0 ? semantic_link_1.LinkUtil.getUri : _c); uri = getUri(resource, rel); if (!uri) return [3 /*break*/, 5]; trackedState = trackedRepresentationUtil_1.TrackedRepresentationUtil.getState(resource); switch (trackedState.status) { case status_1.Status.virtual: log.info('Resource is client-side only and will not be deleted %s %s', uri, trackedState.status.toString()); return [2 /*return*/, resource]; case status_1.Status.deleted: case status_1.Status.deleteInProgress: return [2 /*return*/, Promise.reject(new Error("Resource 'deleted' unable to delete '".concat(uri, "'")))]; case status_1.Status.forbidden: // TODO: enhance forbidden strategy as needed currently assumes forbidden access doesn't change per session log.info('Resource is already forbidden and will not be deleted %s', uri); return [2 /*return*/, resource]; } _d.label = 1; case 1: _d.trys.push([1, 3, , 4]); trackedState.previousStatus = trackedState.status; trackedState.status = status_1.Status.deleteInProgress; return [4 /*yield*/, httpRequestFactory_1.HttpRequestFactory.Instance().del(resource, options)]; case 2: response = _d.sent(); trackedState.status = status_1.Status.deleted; // mutate the original resource headers // how was it retrieved trackedState.headers = this.mergeHeaders(trackedState.headers, response.headers); // save the across-the-wire metadata, so we can check for collisions/staleness trackedState.retrieved = new Date(); return [2 /*return*/, resource]; case 3: e_2 = _d.sent(); if ((0, httpRequestError_1.isHttpRequestError)(e_2)) { this.processError(e_2, uri, resource, trackedState); } return [3 /*break*/, 4]; case 4: return [3 /*break*/, 6]; case 5: log.error('undefined returned on link \'%s\' (check stack trace)', rel); _d.label = 6; case 6: return [3 /*break*/, 8]; case 7: // TODO: decide if we want to make a locationOnly resource if possible and then continue return [2 /*return*/, Promise.reject(new Error("delete tracked representation has no state on '".concat(semantic_link_1.LinkUtil.getUri(resource, linkRelation_1.LinkRelation.Self), "'")))]; case 8: return [2 /*return*/, resource]; } }); }); }; /** * * @throws */ TrackedRepresentationFactory.update = function (resource, document, options) { return __awaiter(this, void 0, void 0, function () { var rel, getUri, throwOnUpdateError, uri, trackedState, response, e_3; var _a, _b, _c, _d; return __generator(this, function (_e) { switch (_e.label) { case 0: if (!(0, instanceOfTrackedRepresentation_1.instanceOfTrackedRepresentation)(resource)) return [3 /*break*/, 8]; rel = (_a = __assign({}, options), _b = _a.rel, _b === void 0 ? linkRelation_1.LinkRelation.Self : _b), getUri = (_c = _a.getUri, _c === void 0 ? semantic_link_1.LinkUtil.getUri : _c), throwOnUpdateError = (_d = _a.throwOnUpdateError, _d === void 0 ? defaultRequestOptions_1.defaultRequestOptions.throwOnUpdateError : _d); uri = getUri(resource, rel); if (!uri) return [3 /*break*/, 6]; trackedState = trackedRepresentationUtil_1.TrackedRepresentationUtil.getState(resource); _e.label = 1; case 1: _e.trys.push([1, 4, , 5]); return [4 /*yield*/, httpRequestFactory_1.HttpRequestFactory.Instance().update(resource, document, options)]; case 2: response = _e.sent(); // mutate the original resource headers // how was it retrieved trackedState.headers = this.mergeHeaders(trackedState.headers, response.headers); // save the across-the-wire metadata, so we can check for collisions/staleness trackedState.previousStatus = trackedState.status; trackedState.status = status_1.Status.hydrated; // when was it retrieved - for later queries trackedState.retrieved = new Date(); return [4 /*yield*/, this.processResource(resource, document, options)]; case 3: return [2 /*return*/, _e.sent()]; case 4: e_3 = _e.sent(); // TODO: add options error type detection factory if ((0, httpRequestError_1.isHttpRequestError)(e_3)) { this.processError(e_3, uri, resource, trackedState); } if (throwOnUpdateError) { throw e_3; } return [3 /*break*/, 5]; case 5: return [3 /*break*/, 7]; case 6: log.error("No link rel found for '".concat(rel, "'")); _e.label = 7; case 7: return [2 /*return*/, resource]; case 8: return [2 /*return*/, Promise.reject(new Error("update tracked representation has no state on '".concat(semantic_link_1.LinkUtil.getUri(resource, linkRelation_1.LinkRelation.Self), "'")))]; } }); }); }; /** * Processes all the hydration rules of the {@link LinkedRepresentation} of whether or not a resource a should * be fetched based on its state and http headers. * * Its responsibility is to deal with the tracking of the representation. * * TODO: load would ideally NOT come in on a TrackedRepresentation but rather a LinkedRepresentation only * * @param resource existing resource * @param options */ TrackedRepresentationFactory.load = function (resource, options) { return __awaiter(this, void 0, void 0, function () { var rel, getUri, includeItems, refreshStaleItems, throwOnLoadError, trackResponse, trackResponseStrategies, uri, trackedState, loadResource, response, trackResponseStrategies_1, trackResponseStrategies_1_1, strategy, e_4, uri, unknown; var _a, _b, _c, _d, _e, _f, _g, _h, e_5, _j; var _this = this; return __generator(this, function (_k) { switch (_k.label) { case 0: if (!(0, instanceOfTrackedRepresentation_1.instanceOfTrackedRepresentation)(resource)) return [3 /*break*/, 15]; rel = (_a = __assign({}, options), _b = _a.rel, _b === void 0 ? linkRelation_1.LinkRelation.Self : _b), getUri = (_c = _a.getUri, _c === void 0 ? semantic_link_1.LinkUtil.getUri : _c), includeItems = (_d = _a.includeItems, _d === void 0 ? false : _d), refreshStaleItems = (_e = _a.refreshStaleItems, _e === void 0 ? true : _e), throwOnLoadError = (_f = _a.throwOnLoadError, _f === void 0 ? defaultRequestOptions_1.defaultRequestOptions.throwOnLoadError : _f), trackResponse = (_g = _a.trackResponse, _g === void 0 ? true : _g), trackResponseStrategies = (_h = _a.trackResponseStrategies, _h === void 0 ? TrackedRepresentationFactory.defaultResponseStrategies : _h); uri = getUri(resource, rel); if (!uri) return [3 /*break*/, 13]; trackedState = trackedRepresentationUtil_1.TrackedRepresentationUtil.getState(resource); switch (trackedState.status) { case status_1.Status.virtual: log.info('Resource is client-side only and will not be fetched %s %s', uri, trackedState.status.toString()); return [2 /*return*/, resource]; case status_1.Status.deleted: log.info('Resource is already deleted and will not be fetched %s', uri); return [2 /*return*/, resource]; case status_1.Status.deleteInProgress: log.info('Resource is being deleted and will not be fetched %s', uri); return [2 /*return*/, resource]; case status_1.Status.forbidden: // TODO: enhance forbidden strategy as needed currently assumes forbidden access doesn't change per session log.info('Resource is already forbidden and will not be fetched %s', uri); return [2 /*return*/, resource]; } if (!(trackedRepresentationUtil_1.TrackedRepresentationUtil.needsFetchFromState(resource, options) || trackedRepresentationUtil_1.TrackedRepresentationUtil.needsFetchFromHeaders(resource, options))) return [3 /*break*/, 8]; _k.label = 1; case 1: _k.trys.push([1, 6, , 7]); loadResource = function (options) { return __awaiter(_this, void 0, void 0, function () { var _a, _b, rel, _c, requestHeadersStrategies, opts, requestHeaders; return __generator(this, function (_d) { switch (_d.label) { case 0: _a = __assign({}, options), _b = _a.rel, rel = _b === void 0 ? linkRelation_1.LinkRelation.Self : _b, _c = _a.requestHeadersStrategies, requestHeadersStrategies = _c === void 0 ? requestHeaders_1.RequestHeaders.defaultStrategies : _c, opts = __rest(_a, ["rel", "requestHeadersStrategies"]); requestHeaders = requestHeadersStrategies.reduce(function (acc, curr) { return (__assign(__assign({}, acc), (curr(resource, opts)))); }, requestHeaders_1.RequestHeaders.emptyHeaders); return [4 /*yield*/, httpRequestFactory_1.HttpRequestFactory .Instance() .load(resource, rel, __assign(__assign({}, options), { headers: requestHeaders }))]; case 1: return [2 /*return*/, _d.sent()]; } }); }); }; return [4 /*yield*/, loadResource(options)]; case 2: response = _k.sent(); // mutate the original resource headers // how was it retrieved trackedState.headers = this.mergeHeaders(trackedState.headers, response.headers); if (!trackedRepresentationUtil_1.TrackedRepresentationUtil.hasFeedETag(resource)) return [3 /*break*/, 4]; if (!trackedRepresentationUtil_1.TrackedRepresentationUtil.hasStaleFeedETag(resource)) return [3 /*break*/, 4]; // feed item is out of date and need to do extra request trackedRepresentationUtil_1.TrackedRepresentationUtil.setStateStaleFromETag(resource); // retry with strategy (ie no cache) log.debug('ETags do not match: load again'); return [4 /*yield*/, loadResource(options)]; case 3: response = _k.sent(); trackedState.headers = this.mergeHeaders(trackedState.headers, response.headers); _k.label = 4; case 4: // save the across-the-wire metadata, so we can check for collisions/staleness trackedState.previousStatus = trackedState.status; trackedState.status = status_1.Status.hydrated; // when was it retrieved - for later queries trackedState.retrieved = new Date(); try { for (trackResponseStrategies_1 = __values(trackResponseStrategies), trackResponseStrategies_1_1 = trackResponseStrategies_1.next(); !trackResponseStrategies_1_1.done; trackResponseStrategies_1_1 = trackResponseStrategies_1.next()) { strategy = trackResponseStrategies_1_1.value; trackedState.representation = strategy(response, trackResponse); } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (trackResponseStrategies_1_1 && !trackResponseStrategies_1_1.done && (_j = trackResponseStrategies_1.return)) _j.call(trackResponseStrategies_1); } finally { if (e_5) throw e_5.error; } } return [4 /*yield*/, this.processResource(resource, response.data, options)]; case 5: return [2 /*return*/, _k.sent()]; case 6: e_4 = _k.sent(); if ((0, httpRequestError_1.isHttpRequestError)(e_4)) { this.processError(e_4, uri, resource, trackedState); } if (throwOnLoadError) { throw e_4; } return [3 /*break*/, 7]; case 7: return [3 /*break*/, 12]; case 8: if (!(0, instanceOfCollection_1.instanceOfCollection)(resource)) return [3 /*break*/, 12]; if (!includeItems) return [3 /*break*/, 10]; return [4 /*yield*/, this.processCollectionItems(resource, options)]; case 9: _k.sent(); return [3 /*break*/, 12]; case 10: if (!refreshStaleItems) return [3 /*break*/, 12]; // otherwise, walk through the collection and ensure stale items are refreshed return [4 /*yield*/, this.processStaleCollectionItems(resource, options)]; case 11: // otherwise, walk through the collection and ensure stale items are refreshed _k.sent(); _k.label = 12; case 12: return [3 /*break*/, 14]; case 13: log.error('undefined returned on link \'%s\' (check stack trace)', rel); _k.label = 14; case 14: return [3 /*break*/, 18]; case 15: uri = semantic_link_1.LinkUtil.getUri(resource, linkRelation_1.LinkRelation.Self); if (!uri) return [3 /*break*/, 17]; log.debug('tracked representation created: unknown on \'%s\'', uri); unknown = sparseRepresentationFactory_1.SparseRepresentationFactory.make(__assign(__assign({}, options), { addStateOn: resource, status: status_1.Status.unknown })); return [4 /*yield*/, TrackedRepresentationFactory.load(unknown, options)]; case 16: return [2 /*return*/, _k.sent()]; case 17: log.error('load tracked representation has no processable uri'); _k.label = 18; case 18: return [2 /*return*/, resource]; } }); }); }; /** * Removes the item from the collection by matching its Self link. If not found, it returns undefined. * If an items is removed from a collection, it is marked as 'stale' */ TrackedRepresentationFactory.removeCollectionItem = function (collection, item) { var itemFromCollection = representationUtil_1.RepresentationUtil.removeItemFromCollection(collection, item); if ((0, instanceOfTrackedRepresentation_1.instanceOfTrackedRepresentation)(itemFromCollection)) { var trackedState = trackedRepresentationUtil_1.TrackedRepresentationUtil.getState(itemFromCollection); trackedState.previousStatus = trackedState.status; trackedState.status = status_1.Status.stale; return itemFromCollection; } return undefined; }; TrackedRepresentationFactory.mergeHeaders = function (trackedHeaders, responseHeaders) { var _a; var eTag = (_a = __assign({}, trackedHeaders)["Etag"], _a === void 0 ? {} : _a); // note: etag may have been added also at the application level of the feed // so retain but override if existing return __assign(__assign({}, eTag), responseHeaders); }; /** * Updates the state object based on the error * * TODO: add client status errors to state for surfacing field validations errors * - this will require an error processing factory given most system * present these errors differently * * TODO: add onErrorHandling strategy (eg throw or quiet) */ TrackedRepresentationFactory.processError = function (e, uri, resource, trackedState) { var response = e.response; if (response) { if (response.status === 403) { log.debug("Request forbidden ".concat(response.status, " ").concat(response.statusText, " '").concat(uri, "'")); // save the across-the-wire metadata, so we can check for collisions/staleness trackedState.status = status_1.Status.forbidden; // when was it retrieved trackedState.retrieved = new Date(); // how was it retrieved trackedState.headers = this.mergeHeaders(trackedState.headers, response.headers); /** * On a forbidden resource we are going to let the decision of what to do with * it lie at the application level. So we'll set the state and return the * resource. This means that the application needs to check if it is {@link Status.forbidden} * and decide whether to remove (from say the set, or in the UI dim the item). */ trackedState.error = e; } else if (response.status === 404) { var message = "Likely stale collection for '".concat(semantic_link_1.LinkUtil.getUri(resource, linkRelation_1.LinkRelation.Self), "' on resource ").concat(uri); log.info(message); trackedState.status = status_1.Status.deleted; // TODO: this should return a Promise.reject for it to be dealt with } else if (response.status >= 400 && response.status < 499) { log.info("Client error '".concat(response.statusText, "' on resource ").concat(uri)); trackedState.status = status_1.Status.unknown; trackedState.error = e; } else if (response.status >= 500 && response.status < 599) { log.info("Server error '".concat(response.statusText, "' on resource ").concat(uri)); trackedState.status = status_1.Status.unknown; trackedState.error = e; } else { log.error("Request error: '".concat(e.message, "'}")); log.debug(e.stack); trackedState.status = status_1.Status.unknown; trackedState.error = e; /** * We really don't know what is happening here. But allow the application * to continue. */ } } }; TrackedRepresentationFactory.processResource = function (resource, data, options) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!(0, instanceOfFeed_1.instanceOfFeed)(data)) return [3 /*break*/, 2]; return [4 /*yield*/, this.processCollection(resource, data, options)]; case 1: return [2 /*return*/, _a.sent()]; case 2: return [4 /*yield*/, this.processSingleton(resource, data, options)]; case 3: return [2 /*return*/, _a.sent()]; } }); }); }; /** * Ensures the in-memory collection resource and its items are up-to-date with the server with * the number of items matching and all items at least sparsely populated. Use 'includeItems' flag * to fully hydrate each item. */ TrackedRepresentationFactory.processCollection = function (resource, data, options) { return __awaiter(this, void 0, void 0, function () { var rel, includeItems, refreshStaleItems, uri, fromFeed; var _a, _b, _c; return __generator(this, function (_d) { switch (_d.label) { case 0: rel = (_a = __assign({}, options), _b = _a.rel, _b === void 0 ? linkRelation_1.LinkRelation.Self : _b), includeItems = _a.includeItems, refreshStaleItems = (_c = _a.refreshStaleItems, _c === void 0 ? true : _c); uri = semantic_link_1.LinkUtil.getUri(resource, rel); if (!uri) { throw new Error('no uri found'); } fromFeed = sparseRepresentationFactory_1.SparseRepresentationFactory.make(__assign(__assign({}, options), { uri: uri, sparseType: 'collection', addStateOn: data })); // merge the existing and the response such that // - any in existing but not in response are removed from existing // - any in response but not in existing are added // the result is to reduce network retrieval because existing hydrated items are not response resource = collectionMerger_1.CollectionMerger.merge(resource, fromFeed, __assign(__assign({}, options), { mergeHeaders: false })); if (!includeItems) return [3 /*break*/, 2]; return [4 /*yield*/, this.processCollectionItems(resource, options)]; case 1: _d.sent(); return [3 /*break*/, 4]; case 2: if (!refreshStaleItems) return [3 /*break*/, 4]; // otherwise, walk through the collection and ensure stale items are refreshed return [4 /*yield*/, this.processStaleCollectionItems(resource, options)]; case 3: // otherwise, walk through the collection and ensure stale items are refreshed _d.sent(); _d.label = 4; case 4: // now merge the collection (attributes) (and do so with observers to trigger) // return SingletonMerger.merge(resource2, representation, options); return [2 /*return*/, resource]; } }); }); }; TrackedRepresentationFactory.processStaleCollectionItems = function (resource, options) { return __awaiter(this, void 0, void 0, function () { var batchSize, waitAll; var _a; var _this = this; return __generator(this, function (_b) { switch (_b.label) { case 0: batchSize = (_a = __assign({}, options).batchSize, _a === void 0 ? 1 : _a); waitAll = (batchSize > 0) ? promiseWaitAll_1.parallelWaitAll : promiseWaitAll_1.sequentialWaitAll; return [4 /*yield*/, waitAll(resource, function (item) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!((0, instanceOfTrackedRepresentation_1.instanceOfTrackedRepresentation)(item) && trackedRepresentationUtil_1.TrackedRepresentationUtil.hasStaleFeedETag(item))) return [3 /*break*/, 2]; return [4 /*yield*/, this.load(item, __assign(__assign({}, options), { rel: linkRelation_1.LinkRelation.Self }))]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/]; } }); }); })]; case 1: _b.sent(); return [2 /*return*/]; } }); }); }; TrackedRepresentationFactory.processCollectionItems = function (resource, options) { return __awaiter(this, void 0, void 0, function () { var forceLoad, forceLoadFeedOnly, batchSize, waitAll; var _a, _b; var _this = this; return __generator(this, function (_c) { switch (_c.label) { case 0: forceLoad = (_a = __assign({}, options), _a.forceLoad), forceLoadFeedOnly = _a.forceLoadFeedOnly, batchSize = (_b = _a.batchSize, _b === void 0 ? 1 : _b); if (forceLoad && forceLoadFeedOnly) { options = __assign(__assign({}, options), { forceLoad: false }); } waitAll = (batchSize > 0) ? promiseWaitAll_1.parallelWaitAll : promiseWaitAll_1.sequentialWaitAll; options = __assign(__assign({}, options), { rel: linkRelation_1.LinkRelation.Self }); return [4 /*yield*/, waitAll(resource, function (item) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.load(item, options)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); })]; case 1: _c.sent(); return [2 /*return*/]; } }); }); }; /** * Ensures the in-memory resource is up-to-date with the server. Synchronising needs to * occur within the context of this {@link State} object so that {@link State.status} flag of * the to-be-retrieved resource is in context. * * Note: singleton also processes a form and may need to be separated for deep merge of items */ TrackedRepresentationFactory.processSingleton = function (resource, representation, options) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, singletonMerger_1.SingletonMerger.merge(resource, representation, options)]; }); }); }; TrackedRepresentationFactory.defaultResponseStrategies = [ /** * Strategy one allows for putting the original response data on to the state object. Can be turned * off by setting {@link ResourceFetchOptions.trackResponse} to false */ function (axiosResponse, trackResponse) { if (trackResponse) { return (0, cloneDetached_1.cloneDetached)(axiosResponse.data); } } ]; return TrackedRepresentationFactory; }()); //# sourceMappingURL=trackedRepresentationFactory.js.map