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
JavaScript
"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