UNPKG

ketting

Version:

Opiniated HATEAOS / Rest client.

143 lines (115 loc) 2.71 kB
import { State } from './state'; import * as qs from 'querystring'; import Client from './client'; import { Field } from './field'; export interface ActionInfo { /** * What url to post the form to. */ uri: string; /** * Action name. * * Some formats call this the 'rel' */ name: string | null; /** * Form title. * * Should be human-friendly. */ title?: string; /** * The HTTP method to use */ method: string; /** * The contentType to use for the form submission */ contentType: string; /** * Returns the list of fields associated to an action */ fields: Field[] } /** * An action represents a hypermedia form submission or action. */ export interface Action<T extends Record<string, any> = Record<string, any>> extends ActionInfo { /** * Execute the action or submit the form. */ submit(formData: T): Promise<State>; } export class SimpleAction<TFormData> implements Action { /** * What url to post the form to. */ uri!: string; /** * Action name. * * Some formats call this the 'rel' */ name!: string | null; /** * Form title. * * Should be human-friendly. */ title!: string; /** * The HTTP method to use */ method!: string; /** * The contentType to use for the form submission */ contentType!: string; /** * Returns the list of fields associated to an action */ fields!: Field[] /** * Reference to client */ client: Client; constructor(client: Client, formInfo: ActionInfo) { this.client = client; for(const [k, v] of Object.entries(formInfo)) { this[k as keyof ActionInfo] = v; } } /** * Execute the action or submit the form. */ async submit(formData: TFormData): Promise<State<any>> { const uri = new URL(this.uri); if (this.method === 'GET') { uri.search = qs.stringify(formData); const resource = this.client.go(uri.toString()); return resource.get(); } let body; switch (this.contentType) { case 'application/x-www-form-urlencoded' : body = qs.stringify(formData); break; case 'application/json': body = JSON.stringify(formData); break; default : throw new Error(`Serializing mimetype ${this.contentType} is not yet supported in actions`); } const response = await this.client.fetcher.fetchOrThrow(uri.toString(), { method: this.method, body, headers: { 'Content-Type': this.contentType } }); const state = this.client.getStateForResponse(uri.toString(), response); return state; } } export class ActionNotFound extends Error {}