diff --git a/source/data/datasource/server/restapi.mjs b/source/data/datasource/server/restapi.mjs index 1732a9ef222b1c358abb9c23f3c441f7aa251c7d..0a341bf2a88923e62a82052a95629908b453e39b 100644 --- a/source/data/datasource/server/restapi.mjs +++ b/source/data/datasource/server/restapi.mjs @@ -12,15 +12,15 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { internalSymbol, instanceSymbol } from "../../../constants.mjs"; -import { isObject, isFunction, isArray } from "../../../types/is.mjs"; -import { diff } from "../../diff.mjs"; -import { Server } from "../server.mjs"; -import { WriteError } from "./restapi/writeerror.mjs"; -import { DataFetchError } from "./restapi/data-fetch-error.mjs"; -import { clone } from "../../../util/clone.mjs"; +import {internalSymbol, instanceSymbol} from "../../../constants.mjs"; +import {isObject, isFunction, isArray} from "../../../types/is.mjs"; +import {diff} from "../../diff.mjs"; +import {Server} from "../server.mjs"; +import {WriteError} from "./restapi/writeerror.mjs"; +import {DataFetchError} from "./restapi/data-fetch-error.mjs"; +import {clone} from "../../../util/clone.mjs"; -export { RestAPI }; +export {RestAPI}; /** * @type {symbol} @@ -28,7 +28,7 @@ export { RestAPI }; * @since 3.12.0 */ const rawDataSymbol = Symbol.for( - "@schukai/monster/data/datasource/server/restapi/rawdata", + "@schukai/monster/data/datasource/server/restapi/rawdata", ); /** @@ -41,159 +41,159 @@ const rawDataSymbol = Symbol.for( * @summary The RestAPI is a class that binds a REST API server. */ class RestAPI extends Server { - /** - * - * @param {Object} [options] options contains definitions for the datasource. - */ - constructor(options) { - super(); - - if (isObject(options)) { - this.setOptions(options); - } - } - - /** - * This method is called by the `instanceof` operator. - * @return {symbol} - * @since 2.1.0 - */ - static get [instanceSymbol]() { - return Symbol.for("@schukai/monster/data/datasource/server/restapi"); - } - - /** - * @property {Object} write={} Options - * @property {Object} write.init={} An option object, containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor} - * @property {string} write.init.method=POST - * @property {Object} write.init.headers Object containing any custom headers that you want to apply to the request. - * @property {string} write.responseCallback Callback function to be executed after the request has been completed. - * @property {string} write.acceptedStatus=[200,201] - * @property {string} write.url URL - * @property {Object} write.mapping the mapping is applied before writing. - * @property {String} write.mapping.transformer Transformer to select the appropriate entries - * @property {exampleCallback[]} write.mapping.callback with the help of the callback, the structures can be adjusted before writing. - * @property {Object} write.report - * @property {String} write.report.path Path to validations - * @property {Object} write.partial - * @property {Function} write.partial.callback Callback function to be executed after the request has been completed. (obj, diffResult) => obj - * @property {Object} write.sheathing - * @property {Object} write.sheathing.object Object to be wrapped - * @property {string} write.sheathing.path Path to the data - * @property {Object} read={} Options - * @property {Object} read.init={} An option object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor} - * @property {string} read.init.method=GET - * @property {array} read.acceptedStatus=[200] - * @property {string} read.url URL - * @property {Object} read.mapping the mapping is applied after reading. - * @property {String} read.mapping.transformer Transformer to select the appropriate entries - * @property {exampleCallback[]} read.mapping.callback with the help of the callback, the structures can be adjusted after reading. - */ - get defaults() { - return Object.assign({}, super.defaults, { - write: { - init: { - method: "POST", - headers: null, - }, - responseCallback: null, - acceptedStatus: [200, 201], - url: null, - mapping: { - transformer: null, - callbacks: [], - }, - sheathing: { - object: null, - path: null, - }, - report: { - path: null, - }, - - partial: { - callback: null, - }, - }, - read: { - init: { - method: "GET", - headers: null, - }, - path: null, - responseCallback: null, - acceptedStatus: [200], - url: null, - mapping: { - transformer: null, - callbacks: [], - }, - }, - }); - } - - /** - * @return {Promise} - * @throws {Error} the options does not contain a valid json definition - * @throws {TypeError} value is not a object - * @throws {Error} the data cannot be read - */ - read() { - let init = this.getOption("read.init"); - if (!isObject(init)) init = {}; - if (!init["method"]) init["method"] = "GET"; - if (typeof init["headers"] !== "object") { - init["headers"] = { - "Accept": "application/json", - }; - } - - let callback = this.getOption("read.responseCallback"); - if (!callback) { - callback = (obj) => { - this.set(this.transformServerPayload.call(this, obj)); - }; - } - - return fetchData.call(this, init, "read", callback); - } - - /** - * @return {Promise} - * @throws {WriteError} the data cannot be written - */ - write() { - let init = this.getOption("write.init"); - if (!isObject(init)) init = {}; - if (typeof init["headers"] !== "object") { - init["headers"] = { - "Content-Type": "application/json", - "Accept": "application/json", - }; - } - if (!init["method"]) init["method"] = "POST"; - - const obj = this.prepareServerPayload(this.get()); - init["body"] = JSON.stringify(obj); - - const callback = this.getOption("write.responseCallback"); - return fetchData.call(this, init, "write", callback); - } - - /** - * @return {RestAPI} - */ - getClone() { - const api = new RestAPI(); - - const read = clone(this[internalSymbol].getRealSubject()["options"].read); - const write = clone(this[internalSymbol].getRealSubject()["options"].write); - - api.setOption("read", read); - api.setOption("write", write); - - return api; - } + /** + * + * @param {Object} [options] options contains definitions for the datasource. + */ + constructor(options) { + super(); + + if (isObject(options)) { + this.setOptions(options); + } + } + + /** + * This method is called by the `instanceof` operator. + * @return {symbol} + * @since 2.1.0 + */ + static get [instanceSymbol]() { + return Symbol.for("@schukai/monster/data/datasource/server/restapi"); + } + + /** + * @property {Object} write={} Options + * @property {Object} write.init={} An option object, containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor} + * @property {string} write.init.method=POST + * @property {Headers} write.init.headers Object containing any custom headers that you want to apply to the request. + * @property {string} write.responseCallback Callback function to be executed after the request has been completed. + * @property {string} write.acceptedStatus=[200,201] + * @property {string} write.url URL + * @property {Object} write.mapping the mapping is applied before writing. + * @property {String} write.mapping.transformer Transformer to select the appropriate entries + * @property {exampleCallback[]} write.mapping.callback with the help of the callback, the structures can be adjusted before writing. + * @property {Object} write.report + * @property {String} write.report.path Path to validations + * @property {Object} write.partial + * @property {Function} write.partial.callback Callback function to be executed after the request has been completed. (obj, diffResult) => obj + * @property {Object} write.sheathing + * @property {Object} write.sheathing.object Object to be wrapped + * @property {string} write.sheathing.path Path to the data + * @property {Object} read={} Options + * @property {Object} read.init={} An option object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor} + * @property {string} read.init.method=GET + * @property {array} read.acceptedStatus=[200] + * @property {string} read.url URL + * @property {Object} read.mapping the mapping is applied after reading. + * @property {String} read.mapping.transformer Transformer to select the appropriate entries + * @property {exampleCallback[]} read.mapping.callback with the help of the callback, the structures can be adjusted after reading. + */ + get defaults() { + return Object.assign({}, super.defaults, { + write: { + init: { + method: "POST", + headers: null, + }, + responseCallback: null, + acceptedStatus: [200, 201], + url: null, + mapping: { + transformer: null, + callbacks: [], + }, + sheathing: { + object: null, + path: null, + }, + report: { + path: null, + }, + + partial: { + callback: null, + }, + }, + read: { + init: { + method: "GET", + headers: null, + }, + path: null, + responseCallback: null, + acceptedStatus: [200], + url: null, + mapping: { + transformer: null, + callbacks: [], + }, + }, + }); + } + + /** + * @return {Promise} + * @throws {Error} the options does not contain a valid json definition + * @throws {TypeError} value is not a object + * @throws {Error} the data cannot be read + */ + read() { + let init = this.getOption("read.init"); + if (!isObject(init)) init = {}; + if (!(init["headers"] instanceof Headers)) { + init["headers"] = new Headers(); + init["headers"].append("Accept", "application/json"); + init["headers"].append("X-Requested-With", "XMLHttpRequest"); + } + + if (!init["method"]) init["method"] = "GET"; + + let callback = this.getOption("read.responseCallback"); + if (!callback) { + callback = (obj) => { + this.set(this.transformServerPayload.call(this, obj)); + }; + } + + return fetchData.call(this, init, "read", callback); + } + + /** + * @return {Promise} + * @throws {WriteError} the data cannot be written + */ + write() { + let init = this.getOption("write.init"); + if (!isObject(init)) init = {}; + if (!(init["headers"] instanceof Headers)) { + init["headers"] = new Headers(); + init["headers"].append("Accept", "application/json"); + init["headers"].append("Content-Type", "application/json"); + } + if (!init["method"]) init["method"] = "POST"; + + const obj = this.prepareServerPayload(this.get()); + init["body"] = JSON.stringify(obj); + + const callback = this.getOption("write.responseCallback"); + return fetchData.call(this, init, "write", callback); + } + + /** + * @return {RestAPI} + */ + getClone() { + const api = new RestAPI(); + + const read = clone(this[internalSymbol].getRealSubject()["options"].read); + const write = clone(this[internalSymbol].getRealSubject()["options"].write); + + api.setOption("read", read); + api.setOption("write", write); + + return api; + } } /** @@ -204,49 +204,49 @@ class RestAPI extends Server { * @return {Promise<string>} */ function fetchData(init, key, callback) { - let response; - - return fetch(this.getOption(`${key}.url`), init) - .then((resp) => { - response = resp; - - const acceptedStatus = this.getOption(`${key}.acceptedStatus`, [200]).map( - Number, - ); - - if (acceptedStatus.indexOf(resp.status) === -1) { - throw new DataFetchError( - `the response does not contain an accepted status (actual: ${resp.status}).`, - response, - ); - } - - return resp.text(); - }) - .then((body) => { - let obj; - - try { - obj = JSON.parse(body); - - response[rawDataSymbol] = obj; - } catch (e) { - if (body.length > 100) { - body = `${body.substring(0, 97)}...`; - } - - throw new DataFetchError( - `the response does not contain a valid json (actual: ${body}).`, - response, - ); - } - - if (callback && isFunction(callback)) { - callback(obj); - } - return response; - }) - .catch((e) => { - throw e; - }); + let response; + + return fetch(this.getOption(`${key}.url`), init) + .then((resp) => { + response = resp; + + const acceptedStatus = this.getOption(`${key}.acceptedStatus`, [200]).map( + Number, + ); + + if (acceptedStatus.indexOf(resp.status) === -1) { + throw new DataFetchError( + `the response does not contain an accepted status (actual: ${resp.status}).`, + response, + ); + } + + return resp.text(); + }) + .then((body) => { + let obj; + + try { + obj = JSON.parse(body); + + response[rawDataSymbol] = obj; + } catch (e) { + if (body.length > 100) { + body = `${body.substring(0, 97)}...`; + } + + throw new DataFetchError( + `the response does not contain a valid json (actual: ${body}).`, + response, + ); + } + + if (callback && isFunction(callback)) { + callback(obj); + } + return response; + }) + .catch((e) => { + throw e; + }); }