@bitmovin/api-sdk
Version:
Bitmovin JS/TS API SDK
232 lines (231 loc) • 8.94 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RestClient = void 0;
exports.copyAndPrepareBody = copyAndPrepareBody;
const urljoin = require("url-join");
const BaseAPI_1 = require("./BaseAPI");
const NullLogger_1 = require("./NullLogger");
const BitmovinErrorBuilder_1 = require("./BitmovinErrorBuilder");
const BASE_URL = 'https://api.bitmovin.com/v1';
function prepareUrlParameterValue(parameterValue) {
if (parameterValue instanceof Date) {
return parameterValue.toISOString().replace(/\.\d+Z/, 'Z');
}
return parameterValue;
}
function queryParams(params) {
if (!params) {
return '';
}
let queryParameterString = '';
let addSeperator = false;
for (const key of Object.keys(params)) {
if (params[key] != null && typeof params[key] !== 'function' && params[key] !== "") {
queryParameterString += (addSeperator ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(prepareUrlParameterValue(params[key]));
addSeperator = true;
}
}
return queryParameterString;
}
function prepareUrl(baseUrl, url, urlParameterMap, queryStringParameters) {
let modifiedUrl = url;
if (urlParameterMap) {
for (const key of Object.keys(urlParameterMap)) {
if (urlParameterMap[key] instanceof Date) {
let date = urlParameterMap[key];
var dd = date.getDate();
var mm = date.getMonth() + 1;
var yyyy = date.getFullYear();
const padStart = input => input < 10 ? '0' + input : new String(input);
urlParameterMap[key] = `${yyyy}-${padStart(mm)}-${padStart(dd)}`;
}
modifiedUrl = modifiedUrl.replace(new RegExp(`{${key}}`), urlParameterMap[key]);
}
}
if (modifiedUrl.search('{|}') > 0) {
throw new Error('After replacing ' + url + ' with parameter map ' + JSON.stringify(urlParameterMap) + ' there are still some placeholders left for replacing. Please make sure to provide a urlParameterMap that replaces all placeholders');
}
modifiedUrl = urljoin(baseUrl, modifiedUrl);
const queryString = queryParams(queryStringParameters);
if (queryString) {
modifiedUrl = urljoin(modifiedUrl, `?${queryString}`);
}
return new URL(modifiedUrl);
}
function copyAndPrepareBody(value) {
if (value == undefined || value == null) {
return undefined;
}
if (value instanceof Date) {
return value;
}
if (isPrimitive(value)) {
return value;
}
if (Array.isArray(value)) {
if (value.length == 0) {
return undefined;
}
return value.map((element) => copyAndPrepareBody(element));
}
const cloned = {};
for (const property of Object.keys(value)) {
const clonedProperty = copyAndPrepareBody(value[property]);
if (clonedProperty == undefined) {
continue;
}
cloned[property] = clonedProperty;
}
return cloned;
}
function isPrimitive(arg) {
var type = typeof arg;
return arg == null || (type != 'object' && type != 'function');
}
class RestClient {
constructor(configuration) {
this.GET = 'GET';
this.PATCH = 'PATCH';
this.POST = 'POST';
this.PUT = 'PUT';
this.DELETE = 'DELETE';
if (!configuration) {
throw new BaseAPI_1.RequiredError('Configuration must be initialized!');
}
if (!configuration.apiKey) {
throw new BaseAPI_1.RequiredError('Api key must be set!');
}
this.apiKey = configuration.apiKey;
this.tenantOrgId = configuration.tenantOrgId;
this.baseUrl = configuration.baseUrl || BASE_URL;
this.fetch = configuration.fetch || ((url, init) => fetch(url, init));
this.logger = configuration.logger || new NullLogger_1.default();
this.headers = configuration.headers;
this.httpHandler = this.buildHttpHandler();
}
buildHttpHandler() {
let handlers = [
new HeaderHandler(this.apiKey, this.tenantOrgId, this.headers),
new ErrorHandler(),
new LoggingHandler(this.logger)
];
const httpHandler = handlers.reduce((innerHandler, outerHandler) => {
outerHandler.innerHandler = innerHandler;
return outerHandler;
}, new FetchHandler(this.fetch));
return httpHandler;
}
patch(url, urlParameterMap, body) {
return this.request(this.PATCH, url, urlParameterMap, body);
}
post(url, urlParameterMap, body) {
return this.request(this.POST, url, urlParameterMap, body);
}
get(url, urlParameterMap, queryStringParameters) {
return this.request(this.GET, url, urlParameterMap, undefined, queryStringParameters);
}
delete(url, urlParameterMap) {
return this.request(this.DELETE, url, urlParameterMap);
}
put(url, urlParameterMap, body) {
return this.request(this.PUT, url, urlParameterMap, body);
}
async request(method, url, urlParameterMap, body, queryStringParameters) {
const requestUrl = prepareUrl(this.baseUrl, url, urlParameterMap, queryStringParameters);
body = copyAndPrepareBody(body);
const request = {
method: method,
url: requestUrl.toString(),
body: JSON.stringify(body),
headers: {}
};
const response = await this.httpHandler.executeRequest(request);
const bodyResponse = await response.text();
if (bodyResponse.length === 0) {
return undefined;
}
try {
const jsonResponse = JSON.parse(bodyResponse);
if (jsonResponse.data && jsonResponse.data.result) {
return jsonResponse.data.result;
}
return undefined;
}
catch (error) {
throw (0, BitmovinErrorBuilder_1.buildBitmovinError)("Response body could not be parsed to JSON", request, response, bodyResponse, error);
}
}
}
exports.RestClient = RestClient;
class DelegatingHandler {
}
class HeaderHandler extends DelegatingHandler {
constructor(apiKey, tenantOrgId, additionalHeaders) {
super();
const headers = {
'X-Api-Key': apiKey,
'X-Api-Client': 'bitmovin-api-sdk-javascript',
'X-Api-Client-Version': '1.276.0',
'Content-Type': 'application/json'
};
if (tenantOrgId) {
headers['X-Tenant-Org-Id'] = tenantOrgId;
}
this.headers = Object.assign(headers, additionalHeaders);
}
async executeRequest(request) {
request.headers = Object.assign(request.headers, this.headers);
return await this.innerHandler.executeRequest(request);
}
}
class LoggingHandler extends DelegatingHandler {
constructor(logger) {
super();
this.logger = logger;
}
async executeRequest(request) {
await this.logger.logRequest(request);
const response = await this.innerHandler.executeRequest(request);
await this.logger.logResponse(response);
return response;
}
}
class ErrorHandler extends DelegatingHandler {
async executeRequest(request) {
let response;
try {
response = await this.innerHandler.executeRequest(request);
if (response.status >= 200 && response.status <= 299) {
return response;
}
}
catch (error) {
throw (0, BitmovinErrorBuilder_1.buildBitmovinErrorFromError)(request, error);
}
const bodyResponse = await response.text();
if (bodyResponse.length === 0) {
throw (0, BitmovinErrorBuilder_1.buildBitmovinError)(`HTTP response code was ${response.status} ${response.statusText} (response body is empty)`, request, response, bodyResponse);
}
let jsonError;
try {
jsonError = JSON.parse(bodyResponse);
}
catch (error) {
throw (0, BitmovinErrorBuilder_1.buildBitmovinError)(`HTTP response code was ${response.status} ${response.statusText} (response body is invalid JSON)`, request, response, bodyResponse);
}
throw (0, BitmovinErrorBuilder_1.buildBitmovinError)(`HTTP response code was ${response.status} ${response.statusText}`, request, response, jsonError);
}
}
class FetchHandler {
constructor(fetch) {
this.fetch = fetch;
}
async executeRequest(request) {
const { url, method, body, headers } = request;
const response = await this.fetch(url, { method, body, headers });
// we always need the body, this way we can access it multiple times
const bodyText = await response.text();
response.text = () => new Promise(resolve => resolve(bodyText));
return response;
}
}