ketting
Version:
Opinionated HATEOAS / Rest client.
174 lines • 5.2 kB
JavaScript
import { BaseState } from './base-state.js';
import { parseLink } from '../http/util.js';
import { Links } from '../link.js';
import { resolve } from '../util/uri.js';
/**
* Represents a resource state in the Siren format
*/
export class SirenState extends BaseState {
/**
* Returns a serialization of the state that can be used in a HTTP
* response.
*
* For example, a JSON object might simply serialize using
* JSON.serialize().
*/
serializeBody() {
throw new Error('Reserializing Siren states is not yet supported. Please log an issue in the Ketting project to help figure out how this should be done');
}
clone() {
return new SirenState({
client: this.client,
uri: this.uri,
data: this.data,
headers: new Headers(this.headers),
links: new Links(this.uri, this.links),
actions: this.actionInfo,
});
}
}
/**
* Turns a HTTP response into a SirenState
*/
export const factory = async (client, uri, response) => {
const body = await response.json();
const links = parseLink(uri, response.headers.get('Link'));
links.add(...parseSirenLinks(uri, body));
return new SirenState({
client,
uri,
data: body.properties,
headers: response.headers,
links: links,
embedded: parseSirenEmbedded(client, uri, body, response.headers),
actions: body.actions ? body.actions.map(action => parseSirenAction(uri, action)) : [],
});
};
function parseSirenLinks(contextUri, body) {
const result = [];
if (body.links !== undefined) {
for (const link of body.links) {
result.push(...parseSirenLink(contextUri, link));
}
}
if (body.entities !== undefined) {
for (const subEntity of body.entities) {
if (subEntity.href !== undefined) {
result.push(...parseSirenLink(contextUri, subEntity));
}
else {
result.push(...parseSirenSubEntityAsLink(contextUri, subEntity));
}
}
}
return result;
}
function parseSirenLink(contextUri, link) {
const result = [];
const { rel: rels, ...attributes } = link;
for (const rel of rels) {
const newLink = {
rel,
context: contextUri,
...attributes,
};
result.push(newLink);
}
return result;
}
function parseSirenEmbedded(client, contextUri, body, headers) {
if (body.entities === undefined) {
return [];
}
const result = [];
for (const entity of body.entities) {
if (isSubEntity(entity)) {
const subState = parseSirenSubEntityAsEmbedded(client, contextUri, entity, headers);
if (subState !== null) {
result.push(subState);
}
}
}
return result;
}
function parseSirenSubEntityAsLink(contextUri, subEntity) {
if (subEntity.links === undefined) {
// We don't yet support subentities that don't have a URI.
return [];
}
let selfHref = null;
for (const link of subEntity.links) {
if (link.rel.includes('self')) {
selfHref = link.href;
}
}
if (selfHref === null) {
// We don't yet support subentities that don't have a URI.
return [];
}
return subEntity.rel.map(rel => {
const title = subEntity.title;
const link = {
href: selfHref,
rel,
context: contextUri,
};
if (title) {
link.title = title;
}
return link;
});
}
function parseSirenSubEntityAsEmbedded(client, contextUri, subEntity, headers) {
if (subEntity.links === undefined) {
// We don't yet support subentities that don't have a URI.
return null;
}
let selfHref = null;
for (const link of subEntity.links) {
if (link.rel.includes('self')) {
selfHref = link.href;
}
}
if (!selfHref) {
// We don't yet support subentities that don't have a URI.
return null;
}
const subEntityUrl = resolve(contextUri, selfHref);
return new SirenState({
client,
uri: subEntityUrl,
data: subEntity.properties,
headers,
links: new Links(selfHref, parseSirenLinks(selfHref, subEntity)),
});
}
function isSubEntity(input) {
return input.href === undefined;
}
function parseSirenAction(uri, action) {
return {
uri: resolve(uri, action.href),
name: action.name,
title: action.title,
method: action.method || 'GET',
contentType: action.type || 'application/x-www-form-urlencoded',
fields: action.fields ? action.fields.map(field => sirenFieldToField(field)) : [],
};
}
function sirenFieldToField(input) {
const result = {
name: input.name,
type: input.type || 'text',
required: false,
readOnly: false,
};
if (input.value) {
result.value = input.value;
}
if (input.title) {
result.label = input.title;
}
return result;
}
//# sourceMappingURL=siren.js.map