Skip to content

Commit

Permalink
feat: read user config at requestManager class creation once only
Browse files Browse the repository at this point in the history
  • Loading branch information
lili2311 committed Jan 31, 2022
1 parent 42096c6 commit 8a09bc1
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 101 deletions.
23 changes: 6 additions & 17 deletions src/lib/request/request.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,12 @@
// 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';

// Fixes issue https://github.com/axios/axios/issues/3384
// 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;
Expand All @@ -39,24 +30,22 @@ const getTopParentModuleName = (parent: NodeModule | null): string => {
const makeSnykRequest = async (
request: SnykRequest,
snykToken = '',
apiUrl = DEFAULT_API,
userAgentPrefix = '',
): Promise<AxiosResponse<any>> => {
const userConfig = getConfig();
const token = snykToken == '' ? userConfig.token : snykToken;

const topParentModuleName = getTopParentModuleName(module.parent as any);
const userAgentPrefixChecked =
userAgentPrefix != '' && !userAgentPrefix.endsWith('/')
? userAgentPrefix + '/'
: userAgentPrefix;
const requestHeaders: Record<string, any> = {
'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 },
});
Expand Down Expand Up @@ -94,4 +83,4 @@ const makeSnykRequest = async (
}
};

export { makeSnykRequest, getConfig, SnykRequest };
export { makeSnykRequest, SnykRequest, DEFAULT_API };
35 changes: 28 additions & 7 deletions src/lib/request/requestManager.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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<QueuedRequest>;
// 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<string, number>;
_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<QueuedRequest>({
burstSize: params?.burstSize || 10,
period: params?.period || 500,
Expand All @@ -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<QueuedRequest>): void => {
Expand All @@ -70,6 +90,7 @@ class RequestsManager {
const response = await makeSnykRequest(
request.snykRequest,
this._snykToken,
this._apiUrl,
this._userAgentPrefix,
);
this._emit({
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -261,4 +282,4 @@ class RequestsManager {
};
}

export { RequestsManager as requestsManager };
export { RequestsManager as requestsManager, getConfig };
116 changes: 49 additions & 67 deletions test/lib/request/request.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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')
Expand All @@ -82,19 +85,22 @@ 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();
});
});

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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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', () => {

This comment has been minimized.

Copy link
@lili2311

lili2311 Jan 31, 2022

Author Contributor

moved to another file

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();
});
});
Loading

0 comments on commit 8a09bc1

Please sign in to comment.