UNPKG

odata

Version:

o.js is a isomorphic Odata Javascript library to simplify the request of data. The main goal is to build a standalone, lightweight and easy to understand Odata lib.

264 lines (247 loc) 7.53 kB
import { OBatch } from "./OBatch"; import { OdataConfig } from "./OdataConfig"; import { OdataQuery } from "./OdataQuery"; import { ORequest } from "./ORequest"; type BodyType = | Blob | BufferSource | FormData | URLSearchParams | string | object | Object; export class OHandler { private requests: ORequest[] = []; constructor(public config: OdataConfig) {} /** * Does a fetch request to the given endpoint and request * all resources in sequent. Tries to parse the result logical * so that no further processing is used. If the result is only one * entity a object is returned, otherwise a array of objects. * * @example * ```typescript * const russell = await o('https://services.odata.org/TripPinRESTierService/') * .get('People("russellwhyte")) * .query(); * * console.log(russell); // shows: { FirstName: "Russell", LastName: "Whyte" [...] } * ``` * * If the request fails with an error code higher then 400 it throws the * Response: * * @example * ```typescript * try { * const unknown = await o('https://services.odata.org/TripPinRESTierService/') * .get('People("unknown")) * .query(); * } catch(res) { // Response * console.log(res.status); // 404 * } * ``` * * @param query The URLSearchParams that are added to the question mark on the url. * That are usually the odata queries like $filter, $top, etc... or a string of parameters. * @returns Either an array or a object with the given entities. If multiple * resources are fetched, this method returns a array of array/object. If there * is no content (e.g. for delete) this method returns the Response */ public async query(query?: OdataQuery | string) { try { this.config.onStart(this); const response: Response[] = await this.getFetch(query); const json = await Promise.all( response.map(async (res) => { if (res.status >= 400) { throw res; } else if (res.ok && res.json) { try { this.config.onFinish(this, res); const data = await res.json(); return data[this.config.fragment] || data; } catch (ex) { return res; } } else { return await res.text(); } }) ); return json.length > 1 ? json : json[0]; } catch (ex) { this.config.onError(this, ex); throw ex; } finally { this.requests = []; } } /** * Request all requests in sequent. Does simply return a Response or Response[] * without any data parsing applied. * * @param query The URLSearchParams that are added to the question mark on the url. * That are usually the odata queries like $filter, $top, etc... */ public async fetch(query?: OdataQuery | string) { try { this.config.onStart(this); const fetch = await this.getFetch(query); return fetch.length === 1 ? fetch[0] : fetch; } catch (ex) { this.config.onError(this, ex); throw ex; } finally { this.config.onFinish(this); this.requests = []; } } /** * Does a batch http-batch request. All request in that sequent are send via one * physically request and afterwards parsed to separate data chunks. * * @param query The URLSearchParams that are added to the question mark on the url. * That are usually the odata queries like $filter, $top, etc... */ public async batch(query?: OdataQuery) { try { const batch = new OBatch(this.requests, this.config, query); const url = this.getUrl(this.config.batch.endpoint); const data = await batch.fetch(url); return data; } catch (ex) { this.config.onError(this, ex); throw ex; } finally { this.requests = []; } } /** * Gets the data from the endpoint + resource url. * * @param resource The resource to request e.g. People/$value. */ public get(resource: string = "") { const url = this.getUrl(resource); const request = new ORequest(url, { ...this.config, method: "GET" }); this.requests.push(request); return this; } /** * Post data to an endpoint + resource. * * @param resource The resource to post to. * @param body The data to post. */ public post(resource: string = "", body: BodyType) { const url = this.getUrl(resource); const request = new ORequest(url, { ...this.config, method: "POST", body: this.getBody(body), }); this.requests.push(request); return this; } /** * Put data to an endpoint + resource. * * @param resource The resource to put to. * @param body The data to put. */ public put(resource: string = "", body: BodyType) { const url = this.getUrl(resource); const request = new ORequest(url, { ...this.config, method: "PUT", body: this.getBody(body), }); this.requests.push(request); return this; } /** * Patch data to an endpoint + resource. * * @param resource The resource to patch to. * @param body The data to patch. */ public patch(resource: string = "", body: BodyType) { const url = this.getUrl(resource); const request = new ORequest(url, { ...this.config, body: this.getBody(body), method: "PATCH", }); this.requests.push(request); return this; } /** * Deletes a resource from the endpoint. * * @param resource The resource to delete e.g. People/1 */ public delete(resource = "") { const url = this.getUrl(resource); const request = new ORequest(url, { ...this.config, method: "DELETE" }); this.requests.push(request); return this; } /** * Use that method to add any kind of request (e.g. a head request) to * the execution list. * * @example * ```typescript * const req = new ORequest('http://full.url/healt', { method: 'HEAD'}); * const res = await o('http://another.url').request(req).fetch(); * console.log(res.status); // e.g. 200 from http://full.url/healt * ``` * @param req The request to add. */ public request(req: ORequest) { this.requests.push(req); return this; } /** * Determines how many request are outstanding. */ public get pending() { return this.requests.length; } /** * Returns a URL based on the rootURL + the given resource * @param resource The resource to join. */ public getUrl(resource: string) { return new URL(resource, this.config.rootUrl); } private async getFetch(query: OdataQuery | string) { if (this.pending > 1) { const result: Response[] = []; for (const req of this.requests) { if (typeof query === "string") { req.applyStringQuery(query, this.config.query); } else { req.applyQuery({ ...this.config.query, ...query }); } const request = await req.fetch; result.push(request); } return result; } else { if (typeof query === "string") { this.requests[0].applyStringQuery(query, this.config.query); } else { this.requests[0].applyQuery({ ...this.config.query, ...query }); } return [await this.requests[0].fetch]; } } private getBody(body: BodyType): any { if (body instanceof Object) { return JSON.stringify(body); } return body; } }