import { fetchWrapper } from './fetchwrapper.js';
import { SlashDBClient } from './slashdbclient.js';
const SDB_BRH_NO_CLIENTOBJ = 'clientObj missing or not valid SlashDBClient object';
const SDB_BRH_INVALID_CONTENT_TYPE = 'Invalid Content-Type header; acceptable values are json,csv,xml,xsd,html';
const SDB_BRH_INVALID_ACCEPT_TYPE = 'Invalid Accept header; acceptable values are json,csv,xml,xsd,html';
const SDB_BRH_INVALID_DATA = 'Data for POST/PUT body missing';
const SDB_BRH_INVALID_HEADER_OBJ = 'Invalid header parameter - must be an object containing key/value pairs';
const SDB_BRH_INVALID_HEADER_VALUE = 'Invalid header value - must be string or number';
/**
* Executes HTTP requests for SlashDB. Base class for `DataDiscoveryResource` and `SQLPassThruQuery` classes.
*/
class BaseRequestHandler {
/**
* Create a `BaseRequestHandler` object for a given SlashDB instance
* @param {SlashDBClient} [clientObj] - a configured `SlashDBClient` object
*/
constructor(clientObj = null) {
if (clientObj instanceof SlashDBClient) {
this.sdbClient = clientObj;
}
// else {
// throw ReferenceError(SDB_BRH_NO_CLIENTOBJ);
// }
this.validMimeTypes = {
json: 'application/json',
csv: 'text/csv',
xml: 'application/xml',
xsd: 'text/xsd',
html: 'text/html'
}
this.acceptHeader = this.validMimeTypes.json;
this.contentTypeHeader = this.validMimeTypes.json;
this.extraHeaders = {};
}
/**
* Sets Accept header value for HTTP requests
* @param {string} format - a valid MIME type (e.g. `'application/json'`), or a key value of the validMimeTypes property in this class (e.g. `'json','csv'`)
* @throws {TypeError} if format missing
* @throws {TypeError} if format is not string
* @returns {BaseRequestHandler} this object
*/
accept(format) {
if (!format) {
throw TypeError(SDB_BRH_INVALID_ACCEPT_TYPE);
}
if (typeof(format) !== 'string') {
throw TypeError(SDB_BRH_INVALID_ACCEPT_TYPE);
}
format = format.toLowerCase()
if (this.validMimeTypes.hasOwnProperty(format)) {
this.acceptHeader = this.validMimeTypes[format];
return this;
}
else {
this.acceptHeader = format;
for (const ct in this.validMimeTypes) {
if (this.validMimeTypes[ct] === format) {
return this;
}
}
console.warn(`Accept-Type '${format}' unknown`);
}
return this;
}
/**
* Sets Content-Type header value for HTTP POST/PUT requests
* @param {string} format - a valid MIME type (e.g. `'application/json'`), or a key value of the validMimeTypes property in this class (e.g. `'json','csv'`)
* @throws {TypeError} if format missing
* @throws {TypeError} if format is not string
* @returns {BaseRequestHandler} this object
*/
contentType(format) {
if (!format) {
throw TypeError(SDB_BRH_INVALID_CONTENT_TYPE);
}
if (typeof(format) !== 'string') {
throw TypeError(SDB_BRH_INVALID_CONTENT_TYPE);
}
format = format.toLowerCase()
if (this.validMimeTypes.hasOwnProperty(format)) {
this.contentTypeHeader = this.validMimeTypes[format];
return this;
}
else {
this.contentTypeHeader = format;
for (const ct in this.validMimeTypes) {
if (this.validMimeTypes[ct] === format) {
return this;
}
}
console.warn(`Content-Type '${format}' unknown`);
}
return this;
}
/**
* Gets authentication common header for HTTP requests
*/
getHeaders(){
let headers = {};
if (this.sdbClient.apiKey) {
headers = {
apiKey: this.sdbClient.apiKey,
Accept: this.acceptHeader,
'Content-Type': this.contentTypeHeader,
...this.extraHeaders
};
} else if (this.sdbClient.basic) {
headers = {
Authorization: "Basic " + this.sdbClient.basic,
Accept: this.acceptHeader,
'Content-Type': this.contentTypeHeader,
...this.extraHeaders
};
} else if (this.sdbClient.ssoCredentials) {
const token = btoa(this.sdbClient.ssoCredentials.id_token)
headers = {
Authorization: "Bearer " + token,
"X-Identity-Provider-Id": this.sdbClient.sso.idpId,
Accept: this.acceptHeader,
'Content-Type': this.contentTypeHeader,
...this.extraHeaders
};
} else {
headers = {
Accept: this.acceptHeader,
'Content-Type': this.contentTypeHeader,
...this.extraHeaders
};
}
return headers;
}
/**
* Sets arbitrary custom header value for HTTP requests
* @param {object} headersObj - an object containing key/value pairs of header properties
* @throws {TypeError} `headersObj` is not an object
* @throws {TypeError} if the value of a key/value pair is not a string or number
* @returns {BaseRequestHandler} this object
*/
setExtraHeaders(headerObj) {
if (typeof(headerObj) === 'object' && !Array.isArray(headerObj) && headerObj !== null) {
for (const key in headerObj) {
if (typeof(headerObj[key]) !== 'string' && typeof(headerObj[key]) !== 'number') {
throw TypeError(SDB_BRH_INVALID_HEADER_VALUE);
}
this.extraHeaders[key] = headerObj[key];
}
}
else {
throw TypeError(SDB_BRH_INVALID_HEADER_OBJ);
}
return this;
}
/**
* Executes HTTP GET request
* @param {string | DataDiscoveryFilter | SQLPassThruFilter} [path] - an optional string containing a URI segment with URL query parameters
* (e.g. `/Customer/FirstName/Tim?distinct=true`), or a `DataDiscoveryFilter/SQLPassThruFilter` object that contains all the query details
* @returns {Promise} promise containing HTTP response status and data
*/
async get(path) {
const url = this._buildEndpointString(path);
let headers = this.getHeaders();
return fetchWrapper('GET', url, undefined, headers);
}
/**
* Executes HTTP POST request
* @param {string | object} data - an object or string containing data values to include in the POST request body. Can be JSON, an object, or CSV/XML formatted string
* @param {string | DataDiscoveryFilter | SQLPassThruFilter} [path] - an optional string containing a URI segment with URL query parameters
* (e.g. `/Customer/FirstName/Tim?distinct=true`), or a `DataDiscoveryFilter/SQLPassThruFilter` object that contains all the query details. Not used under normal
* circumstances.
* @returns {Promise} promise containing HTTP response status and data
*/
// path here is handled slightly differently since it would not be common to specify;
// used in SlashDBClient's config methods
async post(data, path = undefined) {
if (!data) {
throw ReferenceError(SDB_BRH_INVALID_DATA)
}
const url = this._buildEndpointString(path);
let headers = this.getHeaders();
return fetchWrapper('POST', url, data, headers);
}
/**
* Executes HTTP PUT request
* @param {string | object} data - an object or string containing data values to include in the PUT request body. Can be JSON, an object, or CSV/XML formatted string
* @param {string | DataDiscoveryFilter | SQLPassThruFilter | null | undefined} path - a string containing a URI segment with URL query parameters
* (e.g. `/Customer/FirstName/Tim?distinct=true`), or a `DataDiscoveryFilter/SQLPassThruFilter` object that contains all the query details. Set to null or undefined
* if not required.
* @returns {Promise} promise containing HTTP response status and data
*/
async put(path, data) {
if (!data) {
throw ReferenceError(SDB_BRH_INVALID_DATA)
}
const url = this._buildEndpointString(path);
let headers = this.getHeaders();
return fetchWrapper('PUT', url, data, headers);
}
/**
* Executes HTTP DELETE request
* @param {string | DataDiscoveryFilter | SQLPassThruFilter} [path] - an optional string containing a URI segment with URL query parameters
* (e.g. `/Customer/FirstName/Tim?distinct=true`), or a `DataDiscoveryFilter/SQLPassThruFilter` object that contains all the query details
* @returns {Promise} promise containing HTTP response status and data
*/
async delete(path) {
const url = this._buildEndpointString(path);
let headers = this.getHeaders();
return fetchWrapper('DELETE', url, undefined, headers);
}
/**
* Builds the full endpoint to the requested resource. Meant for internal use only. Overloaded in `DataDiscoveryResource/SQLPassThruQuery`
* @param {string} [path] - an optional string containing a URI segment with SQL query parameters/values and URL query parameters
* (e.g. `/FirstName/Tim?distinct=true`)
* @returns {string} the full endpoint
* @throws {ReferenceError} if no `SlashDBClient` object is found attached to this object
* @throws {SyntaxError} if path parameter is an empty string
* @throws {TypeError} if path parameter is neither a string or a `SQLPassThruFilter` object
*/
_buildEndpointString(path) {
if (! this.sdbClient || ! (this.sdbClient instanceof SlashDBClient)) {
throw ReferenceError(SDB_BRH_NO_CLIENTOBJ);
}
let endpoint = '';
if (!path) {
return this.sdbClient.host + endpoint;
}
if (typeof(path) === 'string') {
if (path.trim().length < 1) {
throw SyntaxError(SDB_SPT_INVALID_PATH_EMPTY);
}
// prefix the URL path with fwd slash if it's missing
if (Array.from(path)[0] !== '/') {
path =`/${path}`;
}
}
else {
throw TypeError(SDB_SPT_INVALID_PATH_EMPTY);
}
return this.sdbClient.host + endpoint + path;
}
}
export { BaseRequestHandler }