ketting
Version:
Opiniated HATEAOS / Rest client.
210 lines • 6.54 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FollowPromiseMany = exports.FollowPromiseOne = void 0;
const link_1 = require("./link");
const uri_1 = require("./util/uri");
const uri_template_1 = require("./util/uri-template");
/**
* Base interface for both FollowOne and FollowAll
*/
class FollowPromise {
constructor() {
this.prefetchEnabled = false;
this.preferPushEnabled = false;
this.preferTranscludeEnabled = false;
this.useHeadEnabled = false;
}
preFetch() {
this.prefetchEnabled = true;
return this;
}
preferPush() {
this.preferPushEnabled = true;
return this;
}
preferTransclude() {
this.preferTranscludeEnabled = true;
return this;
}
/**
* Use a HTTP HEAD request to fetch the links.
*
* This is useful when interacting with servers that embed links in Link
* Headers.
*/
useHead() {
this.useHeadEnabled = true;
return this;
}
}
/**
* The FollowPromise class is what's being returned from follow() functions.
*
* It's 'PromiseLike', which means you can treat it like a Promise, and it
* can be awaited. When used as a Promise, it resolves to the Resource object
* that was followed.
*
* In addition to being a Promise<Resource> stand-in, it also exposes other
* functions, namely:
*
* * `follow()` to allow a user to chain several follow() functions to do
* several 'hops' all at once.
* * `followAll()`, allowing a user to call `followAll()` at the end of a
* chain.
*/
class FollowPromiseOne extends FollowPromise {
constructor(resource, rel, variables) {
super();
this.resource = resource;
this.rel = rel;
this.variables = variables;
}
/**
* This 'then' function behaves like a Promise then() function.
*
* This method signature is pretty crazy, but trust that it's pretty much
* like any then() method on a promise.
*/
then(onfulfilled, onrejected) {
return this.fetchLinkedResource().then(onfulfilled, onrejected);
}
/**
* This 'catch' function behaves like a Promise catch() function.
*/
catch(onrejected) {
return this.fetchLinkedResource().then(undefined, onrejected);
}
/**
* Implementation of a Promise.finally function
*/
finally(onfinally) {
return this.then(() => onfinally(), () => onfinally());
}
/**
* Follow another link immediately after following this link.
*
* This allows you to follow several hops of links in one go.
*
* For example: resource.follow('foo').follow('bar');
*/
follow(rel, variables) {
return new FollowPromiseOne(this.fetchLinkedResource(), rel, variables);
}
/**
* Follows a set of links immediately after following this link.
*
* For example: resource.follow('foo').followAll('item');
*/
followAll(rel) {
return new FollowPromiseMany(this.fetchLinkedResource(), rel);
}
/**
* This function does the actual fetching of the linked
* resource.
*/
async fetchLinkedResource() {
const resource = await this.resource;
const headers = {};
if (this.preferPushEnabled) {
headers['Prefer-Push'] = this.rel;
}
if (!this.useHeadEnabled && this.preferTranscludeEnabled) {
headers.Prefer = 'transclude=' + this.rel;
}
let state;
if (this.useHeadEnabled) {
state = await resource.head({ headers });
}
else {
state = await resource.get({
headers
});
}
const link = state.links.get(this.rel);
if (!link)
throw new link_1.LinkNotFound(`Link with rel ${this.rel} on ${state.uri} not found`);
let href;
if (link.templated) {
href = uri_template_1.expand(link, this.variables || {});
}
else {
href = uri_1.resolve(link);
}
const newResource = resource.go(href);
if (this.prefetchEnabled) {
newResource.get().catch(err => {
// eslint-disable-next-line no-console
console.warn('Error while prefetching linked resource', err);
});
}
return newResource;
}
}
exports.FollowPromiseOne = FollowPromiseOne;
/**
*/
class FollowPromiseMany extends FollowPromise {
constructor(resource, rel) {
super();
this.resource = resource;
this.rel = rel;
}
/**
* This 'then' function behaves like a Promise then() function.
*/
then(onfulfilled, onrejected) {
return this.fetchLinkedResources().then(onfulfilled, onrejected);
}
/**
* This 'catch' function behaves like a Promise catch() function.
*/
catch(onrejected) {
return this.fetchLinkedResources().then(undefined, onrejected);
}
/**
* Implementation of a Promise.finally function
*/
finally(onfinally) {
return this.then(() => onfinally(), () => onfinally());
}
/**
* This function does the actual fetching, to obtained the url
* of the linked resource. It returns the Resource object.
*/
async fetchLinkedResources() {
const resource = await this.resource;
const headers = {};
if (this.preferPushEnabled) {
headers['Prefer-Push'] = this.rel;
}
if (!this.useHeadEnabled && this.preferTranscludeEnabled) {
headers.Prefer = 'transclude=' + this.rel;
}
let state;
if (this.useHeadEnabled) {
state = await resource.head({ headers });
}
else {
state = await resource.get({
headers
});
}
const links = state.links.getMany(this.rel);
let href;
const result = [];
for (const link of links) {
href = uri_1.resolve(link);
const newResource = resource.go(href);
result.push(newResource);
if (this.prefetchEnabled) {
newResource.get().catch(err => {
// eslint-disable-next-line no-console
console.warn('Error while prefetching linked resource', err);
});
}
}
return result;
}
}
exports.FollowPromiseMany = FollowPromiseMany;
//# sourceMappingURL=follow-promise.js.map