@aircall/http
Advanced tools
Comparing version 0.1.3 to 0.1.4
@@ -6,2 +6,14 @@ # Change Log | ||
## [0.1.4](http://bitbucket.org/aircall/front-end-modules/compare/@aircall/http@0.1.3...@aircall/http@0.1.4) (2020-04-09) | ||
### Bug Fixes | ||
* prettify ([a4f45d6](http://bitbucket.org/aircall/front-end-modules/commits/a4f45d68053b300f8a5ba933242d7d44b2bda6fd)) | ||
* **http:** use logger singleton consumer instance ([583f2b3](http://bitbucket.org/aircall/front-end-modules/commits/583f2b39d2784bf459e1b6cea06149fc4f645afc)) | ||
## [0.1.3](http://bitbucket.org/aircall/front-end-modules/compare/@aircall/http@0.1.2...@aircall/http@0.1.3) (2020-04-02) | ||
@@ -8,0 +20,0 @@ |
@@ -1,3 +0,3 @@ | ||
import { AxiosError, AxiosPromise, AxiosRequestConfig } from "axios"; | ||
import { HttpServiceOptions, RequestLogPayload, SearchParams, HTTP_LOG_TYPE } from "./typing/HttpService"; | ||
import { AxiosError, AxiosPromise, AxiosRequestConfig } from 'axios'; | ||
import { HttpServiceOptions, RequestLogPayload, SearchParams, HTTP_LOG_TYPE } from './typing/HttpService'; | ||
declare class HttpService { | ||
@@ -7,2 +7,3 @@ private options; | ||
private axiosInstance; | ||
private logger; | ||
private axiosExternalInstance; | ||
@@ -9,0 +10,0 @@ constructor(options: HttpServiceOptions); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const logger_1 = require("@aircall/logger"); | ||
const axios_1 = require("axios"); | ||
@@ -24,3 +23,3 @@ const constants_1 = require("./constants"); | ||
this.getUrlFromConfig = (baseURL, url, httpLogType) => { | ||
if (!url || !baseURL || httpLogType === "REQUEST") { | ||
if (!url || !baseURL || httpLogType === 'REQUEST') { | ||
return url; | ||
@@ -30,3 +29,3 @@ } | ||
// meanwhile request.config.url is only the path | ||
return url.replace(`${baseURL}/`, ""); | ||
return url.replace(`${baseURL}/`, ''); | ||
}; | ||
@@ -37,3 +36,3 @@ /** | ||
this.logRequestInterceptor = (config) => { | ||
logger_1.default.info(`HTTP | ${config.method} | REQUEST`.toUpperCase(), { | ||
this.logger.info(`HTTP | ${config.method} | REQUEST`.toUpperCase(), { | ||
request: HttpService.getRequestLogPayload(config) | ||
@@ -47,3 +46,3 @@ }); | ||
this.logResponseInterceptor = (response) => { | ||
logger_1.default.debug(`HTTP | ${response.config.method} | SUCCESS`.toUpperCase(), { | ||
this.logger.debug(`HTTP | ${response.config.method} | SUCCESS`.toUpperCase(), { | ||
request: HttpService.getRequestLogPayload(response.config), | ||
@@ -60,3 +59,3 @@ response: { response_code: response.status } | ||
(_b = (_a = this.options).logErrorInterceptor) === null || _b === void 0 ? void 0 : _b.call(_a, error); | ||
logger_1.default.error(`HTTP | ${error.config.method} | ERROR`.toUpperCase(), { | ||
this.logger.error(`HTTP | ${error.config.method} | ERROR`.toUpperCase(), { | ||
request: HttpService.getRequestLogPayload(error.config), | ||
@@ -94,6 +93,10 @@ response: { error } | ||
}; | ||
if (!this.options.apiBaseUrl) { | ||
throw new Error("Missing API URL!"); | ||
const { apiBaseUrl, logger } = this.options; | ||
if (!apiBaseUrl) { | ||
throw new Error('Missing API URL!'); | ||
} | ||
const { apiBaseUrl } = this.options; | ||
if (!logger) { | ||
throw new Error('Missing Logger instance!'); | ||
} | ||
this.logger = logger; | ||
/** | ||
@@ -105,4 +108,4 @@ * Create an axios instance with the API base URL | ||
headers: { | ||
"Content-Type": "application/json; charset=UTF-8", | ||
Accept: "application/json, text/plain, */*" | ||
'Content-Type': 'application/json; charset=UTF-8', | ||
Accept: 'application/json, text/plain, */*' | ||
} | ||
@@ -132,6 +135,4 @@ })); | ||
? Object.keys(searchParams) | ||
.map(key => encodeURIComponent(key) + | ||
"=" + | ||
encodeURIComponent(searchParams[key])) | ||
.join("&") | ||
.map(key => encodeURIComponent(key) + '=' + encodeURIComponent(searchParams[key])) | ||
.join('&') | ||
: undefined; | ||
@@ -138,0 +139,0 @@ return queryString ? `${path}?${queryString}` : path; |
@@ -1,2 +0,3 @@ | ||
import { AxiosRequestConfig, AxiosError } from "axios"; | ||
import { AxiosRequestConfig, AxiosError } from 'axios'; | ||
import { Logger } from '@aircall/logger'; | ||
export interface ApiError { | ||
@@ -19,7 +20,8 @@ data: Record<string, string[]>; | ||
export declare type SearchParams = Record<string, Primitive | Primitive[] | null | undefined>; | ||
export declare type HTTP_LOG_TYPE = "REQUEST" | "SUCCESS" | "ERROR"; | ||
export declare type HTTP_LOG_TYPE = 'REQUEST' | 'SUCCESS' | 'ERROR'; | ||
export interface HttpServiceOptions { | ||
apiBaseUrl?: string; | ||
logger?: Logger; | ||
logErrorInterceptor?: (error: AxiosError) => Promise<AxiosError> | void; | ||
} | ||
export {}; |
{ | ||
"name": "@aircall/http", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"main": "dist/index.js", | ||
@@ -14,5 +14,5 @@ "types": "dist/index.d.ts", | ||
}, | ||
"gitHead": "b78b28cbf565454ebe6f37580f3f8df9609bc857", | ||
"gitHead": "6574e120a8b34fe9032587850a0aa3f775c2c808", | ||
"dependencies": { | ||
"@aircall/logger": "^2.3.3", | ||
"@aircall/logger": "^2.3.4", | ||
"axios": "0.19.2" | ||
@@ -19,0 +19,0 @@ }, |
@@ -5,5 +5,5 @@ export enum ErrorCode { | ||
// Rails response when user locked | ||
UNAUTHORIZED_API_RESPONSE_CODE = 401, | ||
UNAUTHORIZED_API_RESPONSE_CODE = 401 | ||
} | ||
export const INVALID_API_RESPONSE_MESSAGE = 'An internal error occured'; |
import axios, { AxiosError } from 'axios'; | ||
import MockAdapter from 'axios-mock-adapter'; | ||
import { | ||
ErrorCode, | ||
INVALID_API_RESPONSE_MESSAGE, | ||
} from './constants'; | ||
import { Logger } from '@aircall/logger'; | ||
import { ErrorCode, INVALID_API_RESPONSE_MESSAGE } from './constants'; | ||
import HttpService from './'; | ||
const logger = new Logger(); | ||
describe('HttpService', () => { | ||
@@ -19,3 +19,3 @@ let httpService: HttpService; | ||
mock = new MockAdapter(axios); | ||
httpService = new HttpService({ apiBaseUrl: BASE_URL}); | ||
httpService = new HttpService({ apiBaseUrl: BASE_URL, logger }); | ||
httpService.setToken('foobar'); | ||
@@ -27,3 +27,3 @@ }); | ||
try { | ||
httpService = new HttpService({ apiBaseUrl: undefined}); | ||
httpService = new HttpService({ apiBaseUrl: undefined, logger }); | ||
} catch (e) { | ||
@@ -33,2 +33,10 @@ expect(e.message).toBe('Missing API URL!'); | ||
}); | ||
it('should throw an error if the class is instantiated without logger', async () => { | ||
try { | ||
httpService = new HttpService({ apiBaseUrl: BASE_URL, logger: undefined }); | ||
} catch (e) { | ||
expect(e.message).toBe('Missing Logger instance!'); | ||
} | ||
}); | ||
}); | ||
@@ -132,3 +140,3 @@ | ||
}); | ||
it('should call axios with PUT verb', async () => { | ||
@@ -197,3 +205,5 @@ const payload = { | ||
// @ts-ignore | ||
expect(httpService.getFirstErrorMessageOnFirstField({ data: { fieldName: null } })).toEqual(null); | ||
expect(httpService.getFirstErrorMessageOnFirstField({ data: { fieldName: null } })).toEqual( | ||
null | ||
); | ||
}); | ||
@@ -203,3 +213,5 @@ | ||
// @ts-ignore | ||
expect(httpService.getFirstErrorMessageOnFirstField({ data: { fieldName: [] } })).toEqual(null); | ||
expect(httpService.getFirstErrorMessageOnFirstField({ data: { fieldName: [] } })).toEqual( | ||
null | ||
); | ||
}); | ||
@@ -283,3 +295,6 @@ | ||
httpService.handleError({ | ||
response: { status: ErrorCode.UNAUTHORIZED_API_RESPONSE_CODE, data: 'Error from the server' } | ||
response: { | ||
status: ErrorCode.UNAUTHORIZED_API_RESPONSE_CODE, | ||
data: 'Error from the server' | ||
} | ||
} as AxiosError); | ||
@@ -334,5 +349,5 @@ } catch (e) { | ||
it('should call logErrorInterceptor if proviced', (done) => { | ||
it('should call logErrorInterceptor if proviced', done => { | ||
const logErrorInterceptor = jest.fn(); | ||
const service = new HttpService({ apiBaseUrl: BASE_URL , logErrorInterceptor }); | ||
const service = new HttpService({ apiBaseUrl: BASE_URL, logErrorInterceptor, logger }); | ||
const error = { config: { method: 'get' } } as AxiosError; | ||
@@ -339,0 +354,0 @@ |
@@ -1,2 +0,1 @@ | ||
import logger from "@aircall/logger"; | ||
import axios, { | ||
@@ -8,11 +7,15 @@ AxiosError, | ||
AxiosResponse | ||
} from "axios"; | ||
} from 'axios'; | ||
import { Logger } from '@aircall/logger'; | ||
import { ErrorCode, INVALID_API_RESPONSE_MESSAGE } from './constants'; | ||
import { | ||
ErrorCode, | ||
INVALID_API_RESPONSE_MESSAGE | ||
} from "./constants"; | ||
import { ApiResponseError, HttpServiceOptions, RequestLogPayload, SearchParams, ApiError, HTTP_LOG_TYPE } from "./typing/HttpService"; | ||
ApiResponseError, | ||
HttpServiceOptions, | ||
RequestLogPayload, | ||
SearchParams, | ||
ApiError, | ||
HTTP_LOG_TYPE | ||
} from './typing/HttpService'; | ||
// Fallback for unsupported error response | ||
@@ -27,10 +30,17 @@ const defaultInvalidApiResponseErr: ApiResponseError = { | ||
private axiosInstance: AxiosInstance; | ||
private logger: Logger; | ||
private axiosExternalInstance: AxiosInstance; | ||
public constructor(private options: HttpServiceOptions) { | ||
if (!this.options.apiBaseUrl) { | ||
throw new Error("Missing API URL!"); | ||
const { apiBaseUrl, logger } = this.options; | ||
if (!apiBaseUrl) { | ||
throw new Error('Missing API URL!'); | ||
} | ||
const { apiBaseUrl } = this.options; | ||
if (!logger) { | ||
throw new Error('Missing Logger instance!'); | ||
} | ||
this.logger = logger; | ||
/** | ||
@@ -43,4 +53,4 @@ * Create an axios instance with the API base URL | ||
headers: { | ||
"Content-Type": "application/json; charset=UTF-8", | ||
Accept: "application/json, text/plain, */*" | ||
'Content-Type': 'application/json; charset=UTF-8', | ||
Accept: 'application/json, text/plain, */*' | ||
} | ||
@@ -59,5 +69,3 @@ }) | ||
this.axiosExternalInstance = axios.create(); | ||
this.axiosExternalInstance.interceptors.request.use( | ||
this.logRequestInterceptor | ||
); | ||
this.axiosExternalInstance.interceptors.request.use(this.logRequestInterceptor); | ||
this.axiosExternalInstance.interceptors.response.use( | ||
@@ -102,3 +110,3 @@ this.logResponseInterceptor, | ||
): string | undefined => { | ||
if (!url || !baseURL || httpLogType === "REQUEST") { | ||
if (!url || !baseURL || httpLogType === 'REQUEST') { | ||
return url; | ||
@@ -109,3 +117,3 @@ } | ||
// meanwhile request.config.url is only the path | ||
return url.replace(`${baseURL}/`, ""); | ||
return url.replace(`${baseURL}/`, ''); | ||
}; | ||
@@ -128,3 +136,3 @@ | ||
private logRequestInterceptor = (config: AxiosRequestConfig): AxiosRequestConfig => { | ||
logger.info(`HTTP | ${config.method} | REQUEST`.toUpperCase(), { | ||
this.logger.info(`HTTP | ${config.method} | REQUEST`.toUpperCase(), { | ||
request: HttpService.getRequestLogPayload(config) | ||
@@ -140,3 +148,3 @@ }); | ||
private logResponseInterceptor = (response: AxiosResponse): AxiosResponse => { | ||
logger.debug(`HTTP | ${response.config.method} | SUCCESS`.toUpperCase(), { | ||
this.logger.debug(`HTTP | ${response.config.method} | SUCCESS`.toUpperCase(), { | ||
request: HttpService.getRequestLogPayload(response.config), | ||
@@ -152,9 +160,6 @@ response: { response_code: response.status } | ||
*/ | ||
public logErrorInterceptor = ( | ||
error: AxiosError | ||
): Promise<AxiosError> => { | ||
public logErrorInterceptor = (error: AxiosError): Promise<AxiosError> => { | ||
this.options.logErrorInterceptor?.(error); | ||
logger.error(`HTTP | ${error.config.method} | ERROR`.toUpperCase(), { | ||
this.logger.error(`HTTP | ${error.config.method} | ERROR`.toUpperCase(), { | ||
request: HttpService.getRequestLogPayload(error.config), | ||
@@ -176,5 +181,3 @@ response: { error } | ||
// that falls out of the range of 2xx | ||
const errorMessage: string | null = this.getFirstErrorMessageOnFirstField( | ||
error.response | ||
); | ||
const errorMessage: string | null = this.getFirstErrorMessageOnFirstField(error.response); | ||
err = { | ||
@@ -206,9 +209,6 @@ code: error.response.status || ErrorCode.INVALID_API_RESPONSE_CODE, | ||
? Object.keys(searchParams) | ||
.map( | ||
key => | ||
encodeURIComponent(key) + | ||
"=" + | ||
encodeURIComponent(searchParams[key] as string) | ||
) | ||
.join("&") | ||
.map( | ||
key => encodeURIComponent(key) + '=' + encodeURIComponent(searchParams[key] as string) | ||
) | ||
.join('&') | ||
: undefined; | ||
@@ -218,6 +218,3 @@ return queryString ? `${path}?${queryString}` : path; | ||
public get( | ||
path: string, | ||
searchParams?: SearchParams | ||
): AxiosPromise<object> { | ||
public get(path: string, searchParams?: SearchParams): AxiosPromise<object> { | ||
return this.axiosInstance | ||
@@ -233,7 +230,3 @@ .get(path, { | ||
public post( | ||
path: string, | ||
payload: {} = {}, | ||
searchParams?: SearchParams | ||
): AxiosPromise<object> { | ||
public post(path: string, payload: {} = {}, searchParams?: SearchParams): AxiosPromise<object> { | ||
return this.axiosInstance | ||
@@ -256,7 +249,3 @@ .post(this.getPathWithParams(path, searchParams), { ...payload }) | ||
public patch( | ||
path: string, | ||
payload: {} = {}, | ||
searchParams?: SearchParams | ||
): Promise<object> { | ||
public patch(path: string, payload: {} = {}, searchParams?: SearchParams): Promise<object> { | ||
return this.axiosInstance | ||
@@ -270,7 +259,3 @@ .patch(this.getPathWithParams(path, searchParams), { | ||
public put( | ||
path: string, | ||
payload: {} = {}, | ||
searchParams?: SearchParams | ||
): Promise<object> { | ||
public put(path: string, payload: {} = {}, searchParams?: SearchParams): Promise<object> { | ||
return this.axiosInstance | ||
@@ -284,7 +269,3 @@ .put(this.getPathWithParams(path, searchParams), { | ||
public delete( | ||
path: string, | ||
payload: {} = {}, | ||
searchParams?: SearchParams | ||
): Promise<object> { | ||
public delete(path: string, payload: {} = {}, searchParams?: SearchParams): Promise<object> { | ||
return this.axiosInstance | ||
@@ -306,5 +287,3 @@ .delete(this.getPathWithParams(path, searchParams), { | ||
*/ | ||
private getFirstErrorMessageOnFirstField( | ||
errorResponse: ApiError | ||
): string | null { | ||
private getFirstErrorMessageOnFirstField(errorResponse: ApiError): string | null { | ||
if (!errorResponse || !errorResponse.data) { | ||
@@ -325,3 +304,2 @@ // No "body.error" | ||
export default HttpService; |
@@ -1,2 +0,3 @@ | ||
import { AxiosRequestConfig, AxiosError } from "axios"; | ||
import { AxiosRequestConfig, AxiosError } from 'axios'; | ||
import { Logger } from '@aircall/logger'; | ||
@@ -25,9 +26,8 @@ // Rails error response | ||
export type HTTP_LOG_TYPE = "REQUEST" | "SUCCESS" | "ERROR"; | ||
export type HTTP_LOG_TYPE = 'REQUEST' | 'SUCCESS' | 'ERROR'; | ||
export interface HttpServiceOptions { | ||
apiBaseUrl?: string; | ||
logErrorInterceptor?: ( | ||
error: AxiosError | ||
) => Promise<AxiosError> | void; | ||
logger?: Logger; | ||
logErrorInterceptor?: (error: AxiosError) => Promise<AxiosError> | void; | ||
} |
Sorry, the diff of this file is not supported yet
43102
907
Updated@aircall/logger@^2.3.4