@opra/client
Version:
Opra Client package
186 lines (185 loc) • 6.05 kB
JavaScript
import { updateErrorMessage } from '@jsopen/objects';
import { ApiDocument, ApiDocumentFactory, OpraSchema, } from '@opra/common';
import { kBackend } from './constants.js';
import { HttpBackend } from './http-backend.js';
import { HttpBundleObservable } from './http-bundle-observable.js';
import { HttpRequestObservable } from './http-request-observable.js';
const SPLIT_BACKSLASH_PATTERN = /^(\/*)(.+)/;
/**
* Base class for OPRA HTTP clients.
*
* @class OpraClientBase
* @abstract
*/
export class HttpClientBase {
/**
* Creates a new instance of HttpClientBase.
*
* @param backend The backend instance to use for requests.
* @protected
*/
constructor(backend) {
Object.defineProperty(this, kBackend, {
enumerable: false,
value: backend,
});
}
/**
* Gets the base service URL.
*/
get serviceUrl() {
return this[kBackend].serviceUrl;
}
/**
* Fetches the API document from the service.
*
* @param options Fetch options.
* @returns A promise that resolves to an ApiDocument.
* @throws {@link Error} if there is an issue fetching or parsing the document.
*/
async fetchDocument(options) {
const documentMap = {};
const getDocument = async (documentId) => {
const req = this.request('$schema', {
headers: new Headers({ accept: 'application/json' }),
});
if (documentId)
req.param('id', documentId);
const body = await req.getBody().catch(e => {
updateErrorMessage(e, 'Error fetching api schema from url (' +
this.serviceUrl +
').\n' +
e.message);
throw e;
});
if (body.references) {
const oldReferences = body.references;
body.references = {};
for (const [ns, obj] of Object.entries(oldReferences)) {
if (documentMap[obj.id] === null)
throw new Error('Circular reference detected');
documentMap[obj.id] = null;
const x = await getDocument(obj.id);
body.references[ns] = documentMap[obj.id] = x;
}
}
return body;
};
const body = await getDocument(options?.documentId);
return await ApiDocumentFactory.createDocument(body).catch(e => {
updateErrorMessage(e, 'Error loading api document.\n' + e.message);
throw e;
});
}
/**
* Creates a new {@link HttpRequestObservable} for a specific path.
*
* @param path The path of the request.
* @param options Request options.
* @returns A new HttpRequestObservable instance.
*/
request(path, options) {
/* Remove leading backslashes */
path = SPLIT_BACKSLASH_PATTERN.exec(path)?.[2] || '';
const observable = new HttpRequestObservable(this[kBackend], {
...options,
method: options?.method || 'GET',
url: new URL(path, this.serviceUrl),
});
if (options?.params)
observable.param(options.params);
return observable;
}
/**
* Sends a DELETE request.
*
* @param path The path of the request.
* @param options Request options.
* @returns A new HttpRequestObservable instance.
*/
delete(path, options) {
return this.request(path, {
...options,
method: 'DELETE',
});
}
/**
* Sends a GET request.
*
* @param path The path of the request.
* @param options Request options.
* @returns A new HttpRequestObservable instance.
*/
get(path, options) {
return this.request(path, {
...options,
method: 'GET',
});
}
/**
* Sends a PATCH request.
*
* @param path The path of the request.
* @param requestBody The request body.
* @param options Request options.
* @returns A new HttpRequestObservable instance.
*/
patch(path, requestBody, options) {
return this.request(path, {
...options,
method: 'PATCH',
body: requestBody,
});
}
/**
* Sends a POST request.
*
* @param path The path of the request.
* @param requestBody The request body.
* @param options Request options.
* @returns A new HttpRequestObservable instance.
*/
post(path, requestBody, options) {
return this.request(path, {
...options,
method: 'POST',
body: requestBody,
});
}
/**
* Sends a PUT request.
*
* @param path The path of the request.
* @param requestBody The request body.
* @param options Request options.
* @returns A new HttpRequestObservable instance.
*/
put(path, requestBody, options) {
return this.request(path, {
...options,
method: 'PUT',
body: requestBody,
});
}
/**
* Bundles multiple requests into a single multipart HTTP request and
* distributes the parsed sub-responses back to callers.
*
* @param requests The list of requests to bundle.
* @returns A {@link HttpBundleObservable} that emits an ordered array of {@link HttpResponse}.
*/
bundle(requests) {
return new HttpBundleObservable(this[kBackend], requests);
}
/**
* Executes a transaction composed of multiple HTTP requests.
*
* @param {HttpRequestObservable<any>[]} requests - An array of HTTP request observables that are part of the transaction.
* @return {HttpBundleObservable} A new observable that represents the bundled transaction.
*/
transaction(requests) {
return new HttpBundleObservable(this[kBackend], requests).param({
transaction: true,
});
}
}