diff --git a/src/lib/request/request.ts b/src/lib/request/request.ts index f58e33d..489b20b 100644 --- a/src/lib/request/request.ts +++ b/src/lib/request/request.ts @@ -1,5 +1,3 @@ -// eslint-disable-next-line @typescript-eslint/no-var-requires -const Configstore = require('@snyk/configstore'); import axios, { AxiosResponse } from 'axios'; import * as Error from '../customErrors/apiError'; @@ -7,15 +5,8 @@ import * as Error from '../customErrors/apiError'; // where HTTPS over HTTP Proxy Fails with 500 handshakefailed on mcafee proxy import 'global-agent/bootstrap'; -const getConfig = (): { endpoint: string; token: string } => { - const snykApiEndpoint: string = - process.env.SNYK_API || - new Configstore('snyk').get('endpoint') || - 'https://snyk.io/api/v1'; - const snykToken = - process.env.SNYK_TOKEN || new Configstore('snyk').get('api'); - return { endpoint: snykApiEndpoint, token: snykToken }; -}; +const DEFAULT_API = 'https://snyk.io/api/v1'; + interface SnykRequest { verb: string; url: string; @@ -39,11 +30,9 @@ const getTopParentModuleName = (parent: NodeModule | null): string => { const makeSnykRequest = async ( request: SnykRequest, snykToken = '', + apiUrl = DEFAULT_API, userAgentPrefix = '', ): Promise> => { - const userConfig = getConfig(); - const token = snykToken == '' ? userConfig.token : snykToken; - const topParentModuleName = getTopParentModuleName(module.parent as any); const userAgentPrefixChecked = userAgentPrefix != '' && !userAgentPrefix.endsWith('/') @@ -51,12 +40,12 @@ const makeSnykRequest = async ( : userAgentPrefix; const requestHeaders: Record = { 'Content-Type': 'application/json', - Authorization: 'token ' + token, + Authorization: 'token ' + snykToken, 'User-Agent': `${topParentModuleName}${userAgentPrefixChecked}tech-services/snyk-request-manager/1.0`, }; const apiClient = axios.create({ - baseURL: userConfig.endpoint, + baseURL: apiUrl, responseType: 'json', headers: { ...requestHeaders, ...request.headers }, }); @@ -94,4 +83,4 @@ const makeSnykRequest = async ( } }; -export { makeSnykRequest, getConfig, SnykRequest }; +export { makeSnykRequest, SnykRequest, DEFAULT_API }; diff --git a/src/lib/request/requestManager.ts b/src/lib/request/requestManager.ts index ff204cb..982295b 100644 --- a/src/lib/request/requestManager.ts +++ b/src/lib/request/requestManager.ts @@ -1,5 +1,8 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const Configstore = require('@snyk/configstore'); + import { LeakyBucketQueue } from 'leaky-bucket-queue'; -import { SnykRequest, makeSnykRequest } from './request'; +import { SnykRequest, makeSnykRequest, DEFAULT_API } from './request'; import { v4 as uuidv4 } from 'uuid'; import * as requestsManagerError from '../customErrors/requestManagerErrors'; @@ -31,17 +34,33 @@ interface RequestsManagerParams { userAgentPrefix?: string; } +const getConfig = (): { endpoint: string; token: string } => { + const snykApiEndpoint: string = + process.env.SNYK_API || + new Configstore('snyk').get('endpoint') || + DEFAULT_API; + const snykToken = + process.env.SNYK_TOKEN || new Configstore('snyk').get('api'); + return { endpoint: snykApiEndpoint, token: snykToken }; +}; + class RequestsManager { _requestsQueue: LeakyBucketQueue; // TODO: Type _events rather than plain obscure object structure _events: any; + _userConfig: { + endpoint: string; + token: string; + }; // loaded user config from configstore + _apiUrl: string; _retryCounter: Map; _MAX_RETRY_COUNT: number; _snykToken: string; - _userAgentPrefix: string; + _userAgentPrefix?: string; //snykToken = '', burstSize = 10, period = 500, maxRetryCount = 5 constructor(params: RequestsManagerParams = {}) { + this._userConfig = getConfig(); this._requestsQueue = new LeakyBucketQueue({ burstSize: params?.burstSize || 10, period: params?.period || 500, @@ -50,8 +69,9 @@ class RequestsManager { this._events = {}; this._retryCounter = new Map(); this._MAX_RETRY_COUNT = params?.maxRetryCount || 5; - this._snykToken = params?.snykToken || ''; - this._userAgentPrefix = params?.userAgentPrefix || ''; + this._snykToken = params?.snykToken ?? this._userConfig.token; + this._apiUrl = this._userConfig.endpoint; + this._userAgentPrefix = params?.userAgentPrefix; } _setupQueueExecutors = (queue: LeakyBucketQueue): void => { @@ -70,6 +90,7 @@ class RequestsManager { const response = await makeSnykRequest( request.snykRequest, this._snykToken, + this._apiUrl, this._userAgentPrefix, ); this._emit({ @@ -156,7 +177,7 @@ class RequestsManager { const callbackBundle = { callback: (originalRequestId: string, data: any) => { - // TODO: couble check this is ok + // TODO: double check this is ok // eslint-disable-next-line @typescript-eslint/no-use-before-define if (requestId == originalRequestId) { this._removeAllListenersForChannel(syncRequestChannel); @@ -167,7 +188,7 @@ class RequestsManager { }; const errorCallbackBundle = { callback: (originalRequestId: string, data: any) => { - // TODO: couble check this is ok + // TODO: double check this is ok // eslint-disable-next-line @typescript-eslint/no-use-before-define if (requestId == originalRequestId) { this._removeAllListenersForChannel(syncRequestChannel); @@ -261,4 +282,4 @@ class RequestsManager { }; } -export { RequestsManager as requestsManager }; +export { RequestsManager as requestsManager, getConfig }; diff --git a/test/lib/request/request.test.ts b/test/lib/request/request.test.ts index 1152042..517926b 100644 --- a/test/lib/request/request.test.ts +++ b/test/lib/request/request.test.ts @@ -1,4 +1,4 @@ -import { makeSnykRequest, getConfig } from '../../../src/lib/request/request'; +import { makeSnykRequest } from '../../../src/lib/request/request'; import * as fs from 'fs'; import * as nock from 'nock'; import * as _ from 'lodash'; @@ -69,7 +69,10 @@ afterEach(() => { describe('Test Snyk Utils make request properly', () => { it('Test GET command on /', async () => { - const response = await makeSnykRequest({ verb: 'GET', url: '/' }); + const response = await makeSnykRequest( + { verb: 'GET', url: '/' }, + 'token123', + ); const fixturesJSON = JSON.parse( fs .readFileSync(fixturesFolderPath + 'apiResponses/general-doc.json') @@ -82,11 +85,14 @@ describe('Test Snyk Utils make request properly', () => { const bodyToSend = { testbody: {}, }; - const response = await makeSnykRequest({ - verb: 'POST', - url: '/', - body: JSON.stringify(bodyToSend), - }); + const response = await makeSnykRequest( + { + verb: 'POST', + url: '/', + body: JSON.stringify(bodyToSend), + }, + 'token123', + ); expect(_.isEqual(response.data, bodyToSend)).toBeTruthy(); }); }); @@ -94,7 +100,7 @@ describe('Test Snyk Utils make request properly', () => { describe('Test Snyk Utils error handling/classification', () => { it('Test NotFoundError on GET command', async () => { try { - await makeSnykRequest({ verb: 'GET', url: '/xyz', body: '' }); + await makeSnykRequest({ verb: 'GET', url: '/xyz', body: '' }, 'token123'); } catch (err) { expect(err.data).toEqual(404); expect(err).toBeInstanceOf(NotFoundError); @@ -106,11 +112,14 @@ describe('Test Snyk Utils error handling/classification', () => { const bodyToSend = { testbody: {}, }; - await makeSnykRequest({ - verb: 'POST', - url: '/xyz', - body: JSON.stringify(bodyToSend), - }); + await makeSnykRequest( + { + verb: 'POST', + url: '/xyz', + body: JSON.stringify(bodyToSend), + }, + 'token123', + ); } catch (err) { expect(err.data).toEqual(404); expect(err).toBeInstanceOf(NotFoundError); @@ -119,7 +128,7 @@ describe('Test Snyk Utils error handling/classification', () => { it('Test ApiError on GET command', async () => { try { - await makeSnykRequest({ verb: 'GET', url: '/apierror' }); + await makeSnykRequest({ verb: 'GET', url: '/apierror' }, 'token123'); } catch (err) { expect(err.data).toEqual(500); expect(err).toBeInstanceOf(ApiError); @@ -130,11 +139,14 @@ describe('Test Snyk Utils error handling/classification', () => { const bodyToSend = { testbody: {}, }; - await makeSnykRequest({ - verb: 'POST', - url: '/apierror', - body: JSON.stringify(bodyToSend), - }); + await makeSnykRequest( + { + verb: 'POST', + url: '/apierror', + body: JSON.stringify(bodyToSend), + }, + 'token123', + ); } catch (err) { expect(err.data).toEqual(500); expect(err).toBeInstanceOf(ApiError); @@ -143,7 +155,7 @@ describe('Test Snyk Utils error handling/classification', () => { it('Test ApiAuthenticationError on GET command', async () => { try { - await makeSnykRequest({ verb: 'GET', url: '/apiautherror' }); + await makeSnykRequest({ verb: 'GET', url: '/apiautherror' }, 'token123'); } catch (err) { expect(err.data).toEqual(401); expect(err).toBeInstanceOf(ApiAuthenticationError); @@ -154,11 +166,14 @@ describe('Test Snyk Utils error handling/classification', () => { const bodyToSend = { testbody: {}, }; - await makeSnykRequest({ - verb: 'POST', - url: '/apiautherror', - body: JSON.stringify(bodyToSend), - }); + await makeSnykRequest( + { + verb: 'POST', + url: '/apiautherror', + body: JSON.stringify(bodyToSend), + }, + 'token123', + ); } catch (err) { expect(err.data).toEqual(401); expect(err).toBeInstanceOf(ApiAuthenticationError); @@ -167,7 +182,7 @@ describe('Test Snyk Utils error handling/classification', () => { it('Test GenericError on GET command', async () => { try { - await makeSnykRequest({ verb: 'GET', url: '/genericerror' }); + await makeSnykRequest({ verb: 'GET', url: '/genericerror' }, 'token123'); } catch (err) { expect(err.data).toEqual(512); expect(err).toBeInstanceOf(GenericError); @@ -178,50 +193,17 @@ describe('Test Snyk Utils error handling/classification', () => { const bodyToSend = { testbody: {}, }; - await makeSnykRequest({ - verb: 'POST', - url: '/genericerror', - body: JSON.stringify(bodyToSend), - }); + await makeSnykRequest( + { + verb: 'POST', + url: '/genericerror', + body: JSON.stringify(bodyToSend), + }, + 'token123', + ); } catch (err) { expect(err.data).toEqual(512); expect(err).toBeInstanceOf(GenericError); } }); }); - -describe('Test getConfig function', () => { - it('Get snyk token via env var', async () => { - process.env.SNYK_TOKEN = '123'; - expect(getConfig().token).toEqual('123'); - }); - - it('Get snyk.io api endpoint default', async () => { - expect(getConfig().endpoint).toEqual('https://snyk.io/api/v1'); - }); - - it('Get snyk api endpoint via env var', async () => { - process.env.SNYK_API = 'API'; - expect(getConfig().endpoint).toEqual('API'); - }); -}); - -describe('Test snykToken override', () => { - it('Test GET command on / with token override', async () => { - process.env.SNYK_TOKEN = '123'; - const response = await makeSnykRequest( - { verb: 'GET', url: '/customtoken' }, - '0987654321', - ); - expect(_.isEqual(response.data, 'token 0987654321')).toBeTruthy(); - }); - - it('Test GET command on / without token override', async () => { - process.env.SNYK_TOKEN = '123'; - const response = await makeSnykRequest({ - verb: 'GET', - url: '/customtoken', - }); - expect(_.isEqual(response.data, 'token 123')).toBeTruthy(); - }); -}); diff --git a/test/lib/requestManager/normal-flows.test.ts b/test/lib/requestManager/normal-flows.test.ts index f5a3b62..214c181 100644 --- a/test/lib/requestManager/normal-flows.test.ts +++ b/test/lib/requestManager/normal-flows.test.ts @@ -1,4 +1,7 @@ -import { requestsManager } from '../../../src/lib/request/requestManager'; +import { + getConfig, + requestsManager, +} from '../../../src/lib/request/requestManager'; import * as fs from 'fs'; import * as nock from 'nock'; import * as _ from 'lodash'; @@ -258,33 +261,50 @@ describe('Testing Request Flows', () => { console.log(err); } }); +}); - it('Single Sync request with no token override', async () => { - process.env.SNYK_TOKEN = '123'; +describe('Testing Request Flows', () => { + it('Single Sync request with token override', async () => { try { + const requestManager = new requestsManager({ snykToken: '0987654321' }); const responseSync = await requestManager.request({ verb: 'GET', url: '/customtoken', }); - expect(responseSync.data).toEqual('token 123'); + + expect(responseSync.data).toEqual('token 0987654321'); } catch (err) { throw new Error(err); } }); -}); -describe('Testing Request Flows', () => { - const requestManager = new requestsManager({ snykToken: '0987654321' }); - it('Single Sync request with token override', async () => { + it('Single Sync request without token override', async () => { + process.env.SNYK_TOKEN = '123'; + const requestManager = new requestsManager(); try { const responseSync = await requestManager.request({ verb: 'GET', url: '/customtoken', }); - - expect(responseSync.data).toEqual('token 0987654321'); + expect(responseSync.data).toEqual('token 123'); } catch (err) { throw new Error(err); } }); }); + +describe('Test getConfig function', () => { + it('Get snyk token via env var', async () => { + process.env.SNYK_TOKEN = '123'; + expect(getConfig().token).toEqual('123'); + }); + + it('Get snyk.io api endpoint default', async () => { + expect(getConfig().endpoint).toEqual('https://snyk.io/api/v1'); + }); + + it('Get snyk api endpoint via env var', async () => { + process.env.SNYK_API = 'API'; + expect(getConfig().endpoint).toEqual('API'); + }); +});