ibm-cloud-sdk-core
Version:
Core functionality to support SDKs generated with IBM's OpenAPI SDK Generator.
302 lines (301 loc) • 13.5 kB
JavaScript
/**
* (C) Copyright IBM Corp. 2014, 2024.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseService = void 0;
var extend_1 = __importDefault(require("extend"));
var auth_1 = require("../auth");
var helper_1 = require("./helper");
var logger_1 = __importDefault(require("./logger"));
var request_wrapper_1 = require("./request-wrapper");
var build_user_agent_1 = require("./build-user-agent");
/**
* Common functionality shared by generated service classes.
*
* The base service authenticates requests via its authenticator, and sends
* them to the service endpoint.
*/
var BaseService = /** @class */ (function () {
/**
* Configuration values for a service.
*
* @param userOptions - the configuration options to set on the service instance.
* This should be an object with the following fields:
* - authenticator: (required) an Object used to authenticate requests to the service.
* - serviceUrl: (optional) the base url to use when contacting the service.
* The base url may differ between IBM Cloud regions.
* - headers: (optional) a set of HTTP headers that should be included with every request sent to the service
* - disableSslVerification: (optional) a flag that indicates whether verification of the server's SSL certificate should be
* disabled or not.
*/
function BaseService(userOptions) {
if (!(this instanceof BaseService)) {
var err = 'the "new" keyword is required to create service instances';
logger_1.default.error("Error creating an instance of BaseService: ".concat(err));
throw new Error(err);
}
var baseServiceOptions = {};
var options = __assign({}, userOptions);
// for compatibility
if (options.url && !options.serviceUrl) {
options.serviceUrl = options.url;
}
if (options.serviceUrl) {
baseServiceOptions.serviceUrl = (0, helper_1.stripTrailingSlash)(options.serviceUrl);
}
// check serviceUrl for common user errors
var credentialProblems = (0, auth_1.checkCredentials)(options, ['serviceUrl']);
if (credentialProblems) {
logger_1.default.error(credentialProblems.message);
throw credentialProblems;
}
// if disableSslVerification is not explicity set to the boolean value `true`,
// force it to be false
if (options.disableSslVerification !== true) {
options.disableSslVerification = false;
}
var serviceClass = this.constructor;
this.baseOptions = __assign(__assign({ qs: {}, serviceUrl: serviceClass.DEFAULT_SERVICE_URL }, options), baseServiceOptions);
this.requestWrapperInstance = new request_wrapper_1.RequestWrapper(this.baseOptions);
// enforce that an authenticator is set
if (!options.authenticator) {
throw new Error('Authenticator must be set.');
}
this.authenticator = options.authenticator;
this.defaultUserAgent = (0, build_user_agent_1.buildUserAgent)();
}
/**
* Get the instance of the authenticator set on the service.
*
* @returns the Authenticator instance
*/
BaseService.prototype.getAuthenticator = function () {
return this.authenticator;
};
/**
* Set the service URL to send requests to.
*
* @param url - the base URL for the service.
*/
BaseService.prototype.setServiceUrl = function (url) {
if (url) {
this.baseOptions.serviceUrl = (0, helper_1.stripTrailingSlash)(url);
logger_1.default.debug("Set service URL: ".concat(this.baseOptions.serviceUrl));
}
};
/**
* Set the HTTP headers to be sent in every request.
*
* @param headers - the map of headers to include in requests.
*/
BaseService.prototype.setDefaultHeaders = function (headers) {
if (typeof headers !== 'object') {
// do nothing, for now
return;
}
this.baseOptions.headers = headers;
};
/**
* Turn request body compression on or off.
*
* @param setting - Will turn it on if 'true', off if 'false'.
*/
BaseService.prototype.setEnableGzipCompression = function (setting) {
this.requestWrapperInstance.setCompressRequestData(setting);
// persist setting so that baseOptions accurately reflects the state of the flag
this.baseOptions.enableGzipCompression = setting;
};
/**
* Get the Axios instance set on the service.
* All requests will be made using this instance.
*/
BaseService.prototype.getHttpClient = function () {
return this.requestWrapperInstance.getHttpClient();
};
/**
* Enable retries for unfulfilled requests.
*
* @param retryOptions - the configuration for retries
*/
BaseService.prototype.enableRetries = function (retryOptions) {
this.requestWrapperInstance.enableRetries(retryOptions);
};
/**
* Disables retries.
*/
BaseService.prototype.disableRetries = function () {
this.requestWrapperInstance.disableRetries();
};
/**
* Applies a given modifier function on a model object.
* Since the model object can be a map, or an array, or a model,
* these types needs different handling.
* Considering whether the input object is a map happens with an explicit parameter.
* @param input - the input model object
* @param converterFn - the function that is applied on the input object
* @param isMap - is `true` when the input object should be handled as a map
*/
BaseService.convertModel = function (input, converterFn, isMap) {
if (input == null || typeof input === 'string') {
// no need for conversation
return input;
}
if (Array.isArray(input)) {
return BaseService.convertArray(input, converterFn, isMap);
}
else if (isMap === true) {
return BaseService.convertMap(input, converterFn);
}
return converterFn(input);
};
/**
* Configure the service using external configuration
*
* @param serviceName - the name of the service. This will be used to read from external
* configuration.
*/
BaseService.prototype.configureService = function (serviceName) {
logger_1.default.debug("Configuring BaseService instance with service name: ".concat(serviceName));
if (!serviceName) {
var err = 'Error configuring service. Service name is required.';
logger_1.default.error(err);
throw new Error(err);
}
Object.assign(this.baseOptions, this.readOptionsFromExternalConfig(serviceName));
// overwrite the requestWrapperInstance with the new base options if applicable
this.requestWrapperInstance = new request_wrapper_1.RequestWrapper(this.baseOptions);
};
/**
* Wrapper around `sendRequest` that enforces the request will be authenticated.
*
* @param parameters - Service request options passed in by user.
* This should be an object with the following fields:
* - options.method: the http method
* - options.url: the path portion of the URL to be appended to the serviceUrl
* - options.path: the path parameters to be inserted into the URL
* - options.qs: the querystring to be included in the URL
* - options.body: the data to be sent as the request body
* - options.form: an object containing the key/value pairs for a www-form-urlencoded request.
* - options.formData: an object containing the contents for a multipart/form-data request
* The following processing is performed on formData values:
* - string: no special processing -- the value is sent as is
* - object: the value is converted to a JSON string before insertion into the form body
* - NodeJS.ReadableStream|Buffer|FileWithMetadata: sent as a file, with any associated metadata
* - array: each element of the array is sent as a separate form part using any special processing as described above
* - defaultOptions.serviceUrl: the base URL of the service
* - defaultOptions.headers: additional HTTP headers to be sent with the request
* @returns a Promise
*/
BaseService.prototype.createRequest = function (parameters) {
var _this = this;
// validate serviceUrl parameter has been set
var serviceUrl = parameters.defaultOptions && parameters.defaultOptions.serviceUrl;
if (!serviceUrl || typeof serviceUrl !== 'string') {
return Promise.reject(new Error('The service URL is required'));
}
// make sure the outbound request contains a User-Agent header
var userAgent = {
'User-Agent': this.defaultUserAgent,
};
parameters.defaultOptions.headers = (0, extend_1.default)(true, {}, userAgent, parameters.defaultOptions.headers);
return this.authenticator.authenticate(parameters.defaultOptions).then(function () {
// resolve() handles rejection as well, so resolving the result of sendRequest should allow for proper handling later
return _this.requestWrapperInstance.sendRequest(parameters);
});
};
/**
* Wrapper around `createRequest` that enforces arrived response to be deserialized.
* @param parameters - see `parameters` in `createRequest`
* @param deserializerFn - the deserializer function that is applied on the response object
* @param isMap - is `true` when the response object should be handled as a map
* @returns a Promise
*/
BaseService.prototype.createRequestAndDeserializeResponse = function (parameters, deserializerFn, isMap) {
var _this = this;
return new Promise(function (resolve, reject) {
_this.createRequest(parameters)
.then(function (r) {
if (r !== undefined && r.result !== undefined) {
r.result = BaseService.convertModel(r.result, deserializerFn, isMap);
}
resolve(r);
})
.catch(function (err) { return reject(err); });
});
};
// eslint-disable-next-line class-methods-use-this
BaseService.prototype.readOptionsFromExternalConfig = function (serviceName) {
var results = {};
var properties = (0, auth_1.readExternalSources)(serviceName);
if (properties !== null) {
// the user can define the following client-level variables in the credentials file:
// - url
// - disableSsl
// - enableGzip
var url = properties.url, disableSsl = properties.disableSsl, enableGzip = properties.enableGzip, enableRetries = properties.enableRetries, maxRetries = properties.maxRetries, retryInterval = properties.retryInterval;
if (url) {
results.serviceUrl = (0, helper_1.stripTrailingSlash)(url);
}
if (disableSsl === true) {
results.disableSslVerification = disableSsl;
}
if (enableGzip === true) {
results.enableGzipCompression = enableGzip;
}
if (enableRetries !== undefined) {
results.enableRetries = enableRetries;
}
if (maxRetries !== undefined) {
results.maxRetries = maxRetries;
}
if (retryInterval !== undefined) {
results.retryInterval = retryInterval;
}
}
return results;
};
BaseService.convertArray = function (arrayInput, converterFn, isMap) {
var _this = this;
var serializedList = [];
arrayInput.forEach(function (element) {
serializedList.push(_this.convertModel(element, converterFn, isMap));
});
return serializedList;
};
BaseService.convertMap = function (mapInput, converterFn) {
var serializedMap = {};
Object.keys(mapInput).forEach(function (key) {
serializedMap[key] = BaseService.convertModel(mapInput[key], converterFn);
});
return serializedMap;
};
return BaseService;
}());
exports.BaseService = BaseService;
;