import { headerHelper, IResponse, IHeader, IHttpService, IQueryParameter, ISDKInfo, urlHelper } from '@kontent-ai/core-sdk'; import { AxiosError } from 'axios'; import { IManagementClientConfig } from '../config/imanagement-client-config.interface'; import { SharedContracts } from '../contracts'; import { IContentManagementInternalQueryConfig, IContentManagementQueryConfig, SharedModels } from '../models'; import { getType } from 'mime'; export abstract class BaseManagementQueryService { /** * Default base url for content management API */ private readonly defaultBaseCMUrl: string = 'https://manage.kontent.ai/v2'; constructor( protected readonly config: IManagementClientConfig, protected readonly httpService: IHttpService, protected readonly sdkInfo: ISDKInfo ) {} /** * Gets url based on the action, query configuration and options (parameters) * @param action Action (= url part) that will be hit * @param options Query options * @param addSlash Indicates if slash is added to query */ getFullUrl(action: string, options?: IQueryParameter[], addSlash: boolean = true): string { return urlHelper.addOptionsToUrl(this.getBaseUrl() + (addSlash ? '/' : '') + action, options); } /** * Gets proper set of headers for given request. * @param config Query config */ getHeaders(config: IContentManagementQueryConfig): IHeader[] { const headers: IHeader[] = [ // sdk tracking header headerHelper.getSdkIdHeader({ host: this.sdkInfo.host, name: this.sdkInfo.name, version: this.sdkInfo.version }), // add authorization header this.getAuthorizationHeader() ]; // add query headers headers.push(...config.headers); return headers; } /** * Http PATCH response * @param url Url of request * @param config Query configuration */ protected async patchResponseAsync( url: string, body: any, internalConfig: IContentManagementInternalQueryConfig, config: IContentManagementQueryConfig ): Promise> { try { return await this.httpService.patchAsync( { url: url, body: body }, { cancelToken: config.cancelTokenRequest, retryStrategy: this.config.retryStrategy, headers: this.getHeaders(config), responseType: internalConfig && internalConfig.responseType ? internalConfig.responseType : undefined } ); } catch (error) { throw this.mapContentManagementError(error); } } /** * Http GET response * @param url Url of request * @param config Query configuration */ protected async getResponseAsync( url: string, internalConfig: IContentManagementInternalQueryConfig, config: IContentManagementQueryConfig ): Promise> { try { return await this.httpService.getAsync( { url: url }, { cancelToken: config.cancelTokenRequest, retryStrategy: this.config.retryStrategy, headers: this.getHeaders(config), responseType: internalConfig && internalConfig.responseType ? internalConfig.responseType : undefined } ); } catch (err) { throw this.mapContentManagementError(err); } } /** * Http POST response * @param url Url of request * @param body Body of the request (names and values) * @param config Query configuration */ protected async postResponseAsync( url: string, body: any, internalConfig: IContentManagementInternalQueryConfig, config: IContentManagementQueryConfig ): Promise> { try { return await this.httpService.postAsync( { url: url, body: body }, { cancelToken: config.cancelTokenRequest, retryStrategy: this.config.retryStrategy, headers: this.getHeaders(config), responseType: internalConfig && internalConfig.responseType ? internalConfig.responseType : undefined } ); } catch (err) { throw this.mapContentManagementError(err); } } /** * Http PUT response * @param url Url of request * @param body Body of the request (names and values) * @param config Query configuration */ protected async putResponseAsync( url: string, body: any, internalConfig: IContentManagementInternalQueryConfig, config: IContentManagementQueryConfig ): Promise> { try { return await this.httpService.putAsync( { url: url, body: body }, { cancelToken: config.cancelTokenRequest, retryStrategy: this.config.retryStrategy, headers: this.getHeaders(config), responseType: internalConfig && internalConfig.responseType ? internalConfig.responseType : undefined } ); } catch (err) { throw this.mapContentManagementError(err); } } /** * Http Delete response * @param url Url of request * @param body Body of the request (names and values) * @param config Query configuration */ protected async deleteResponseAsync( url: string, internalConfig: IContentManagementInternalQueryConfig, config: IContentManagementQueryConfig ): Promise> { try { return await this.httpService.deleteAsync( { url: url }, { cancelToken: config.cancelTokenRequest, retryStrategy: this.config.retryStrategy, headers: this.getHeaders(config), responseType: internalConfig && internalConfig.responseType ? internalConfig.responseType : undefined } ); } catch (err) { throw this.mapContentManagementError(err); } } protected async getBinaryDataFromUrlAsync(url: string): Promise { // temp fix for repository not validating url url = url.replace('#', '%23'); const response = await this.httpService.getAsync( { url: url }, { responseType: 'arraybuffer' } ); return response.data; } protected getMimeTypeFromFilename(filename: string): string | null { return getType(filename); } private mapContentManagementError(error: any): any { let axiosError: AxiosError | undefined; if (error.error) { axiosError = error.error; } else { axiosError = error; } if (!axiosError || !axiosError.isAxiosError) { return error; } const cmError = axiosError.response?.data as SharedContracts.IContentManagementError; if (cmError?.error_code || cmError?.request_id) { const validationErrors: SharedModels.ValidationError[] = []; if (cmError.validation_errors) { validationErrors.push( ...cmError.validation_errors.map( (validationErrorRaw) => new SharedModels.ValidationError({ message: validationErrorRaw.message }) ) ); } return new SharedModels.ContentManagementBaseKontentError({ errorCode: cmError.error_code, message: cmError.message, originalError: error, requestId: cmError.request_id, validationErrors: validationErrors }); } return error; } /** * Gets authorization header */ private getAuthorizationHeader(): IHeader { const key: string = this.config.apiKey; if (!key) { throw Error(`Cannot get authorization header for query type because API Key is undefined`); } return { header: 'authorization', value: `bearer ${key}` }; } /** * Gets base URL of the request including the project Id */ private getBaseUrl(): string { if (this.config.baseUrl) { return this.config.baseUrl; } return this.defaultBaseCMUrl; } }