ketting
Version:
Opinionated HATEOAS / Rest client.
200 lines • 5.95 kB
JavaScript
/**
* Base interface for both FollowOne and FollowAll
*/
class FollowPromise {
prefetchEnabled;
preferTranscludeEnabled;
useHeadEnabled;
constructor() {
this.prefetchEnabled = false;
this.preferTranscludeEnabled = false;
this.useHeadEnabled = false;
}
preFetch() {
this.prefetchEnabled = 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.
*/
export class FollowPromiseOne extends FollowPromise {
resource;
rel;
variables;
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);
}
/**
* Gets the current state of the resource.
*
* This function will return a State object.
*/
async get(getOptions) {
return (await this).get(getOptions);
}
/**
* 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.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 newResource = state.follow(this.rel, this.variables);
if (this.prefetchEnabled) {
newResource.get().catch((err) => {
// eslint-disable-next-line no-console
console.warn('Error while prefetching linked resource', err);
});
}
return newResource;
}
}
/**
*/
export class FollowPromiseMany extends FollowPromise {
resource;
rel;
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());
}
/**
* Gets the current states of the resources.
*
* This function will return an array of State object.
*/
async get(getOptions) {
return Promise.all((await this).map(resource => resource.get(getOptions)));
}
/**
* 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.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 result = state.followAll(this.rel);
if (this.prefetchEnabled) {
result.map(resource => {
resource.get().catch(err => {
// eslint-disable-next-line no-console
console.warn('Error while prefetching linked resource', err);
});
});
}
return result;
}
}
//# sourceMappingURL=follow-promise.js.map