Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: console warning added when using an invalid API key #1077

Merged
merged 2 commits into from
Mar 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 19 additions & 49 deletions packages/client/src/classes/client.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
'use strict';

/**
* Dependencies
*/
const axios = require('axios');
const pkg = require('../../package.json');
const {
Expand All @@ -15,26 +11,18 @@ const {
},
} = require('@sendgrid/helpers');

/**
* Twilio SendGrid REST Client
*/
const API_KEY_PREFIX = 'SG.';

class Client {
/**
* Constructor
*/
constructor() {

//API key
this.apiKey = '';

//Default headers
this.defaultHeaders = {
'Accept': 'application/json',
Accept: 'application/json',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we convert to a non-string key here? Why only for the Accept key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quotes aren't needed here and is an eslint warning.

'Content-Type': 'application/json',
'User-agent': 'sendgrid/' + pkg.version + ';nodejs',
};

//Empty default request
this.defaultRequest = {
baseUrl: 'https://api.sendgrid.com/',
url: '',
Expand All @@ -43,51 +31,45 @@ class Client {
};
}

/**
* Set API key
*/
setApiKey(apiKey) {
this.apiKey = apiKey;

if (!this.isValidApiKey(apiKey)) {
console.warn(`API key does not start with "${API_KEY_PREFIX}".`);
}
}

isValidApiKey(apiKey) {
return this.isString(apiKey) && apiKey.trim().startsWith(API_KEY_PREFIX);
}

isString(value) {
return typeof value === 'string' || value instanceof String;
}

/**
* Set default header
*/
setDefaultHeader(key, value) {
this.defaultHeaders[key] = value;
return this;
}

/**
* Set default request
*/
setDefaultRequest(key, value) {
this.defaultRequest[key] = value;
return this;
}

/**
* Create headers for request
*/
createHeaders(data) {

//Merge data with default headers
// Merge data with default headers.
const headers = mergeData(this.defaultHeaders, data);

//Add API key, but don't overwrite if header already set
// Add API key, but don't overwrite if header already set.
if (typeof headers.Authorization === 'undefined' && this.apiKey) {
headers.Authorization = 'Bearer ' + this.apiKey;
}

//Return
return headers;
}

/**
* Create request
*/
createRequest(data) {

let options = {
url: data.uri || data.url,
baseUrl: data.baseUrl,
Expand All @@ -97,7 +79,7 @@ class Client {
headers: data.headers,
};

//Merge data with empty request
// Merge data with default request.
options = mergeData(this.defaultRequest, options);
options.headers = this.createHeaders(options.headers);
options.baseURL = options.baseUrl;
Expand All @@ -106,52 +88,40 @@ class Client {
return options;
}

/**
* Do a request
*/
request(data, cb) {

//Create request
data = this.createRequest(data);

//Perform request
const promise = new Promise((resolve, reject) => {
axios(data)
.then(response => {
// Successful response
return resolve([
new Response(response.status, response.data, response.headers),
response.data,
]);
})
.catch(error => {
// Response error
if (error.response) {
if (error.response.status >= 400) {
return reject(new ResponseError(error.response));
}
}
// Request error
return reject(error);
});
});

// Throw and error incase function not passed
// Throw an error in case a callback function was not passed.
if (cb && typeof cb !== 'function') {
throw new Error('Callback passed is not a function.');
}

//Execute callback if provided
if (cb) {
return promise
.then(result => cb(null, result))
.catch(error => cb(error, null));
}

//Return promise
return promise;
}
}

//Export class
module.exports = Client;
29 changes: 26 additions & 3 deletions packages/client/src/client.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,32 @@ const testRequest = (request, statusCode) => {
});
};

/**
* Tests
*/
describe('client', () => {
const sgClient = require('./client');

describe('setApiKey', () => {
let consoleWarnSpy;

beforeEach(() => {
consoleWarnSpy = sinon.spy(console, 'warn');
});

afterEach(() => {
console.warn.restore();
});

it('should not log a warning for a proper API key value', () => {
sgClient.setApiKey('SG.1234567890');
expect(consoleWarnSpy.notCalled).to.equal(true);
});

it('should log a warning for an undefined API key value', () => {
sgClient.setApiKey(undefined);
expect(consoleWarnSpy.calledOnce).to.equal(true);
});
});
});

describe('test_access_settings_activity_get', () => {
const request = {};
request.qs = {
Expand Down