Skip to content

Commit

Permalink
Merge pull request #101 from getyoti/development
Browse files Browse the repository at this point in the history
Release 3.7.2
  • Loading branch information
davidgrayston authored Oct 3, 2019
2 parents 54324aa + b804e55 commit bd5316a
Show file tree
Hide file tree
Showing 11 changed files with 310 additions and 14 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "yoti",
"version": "3.7.1",
"version": "3.7.2",
"description": "Yoti NodeJS SDK for back-end integration",
"author": "Yoti LTD <[email protected]> (https://www.yoti.com/developers)",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
sonar.projectKey = yoti-web-sdk:node
sonar.projectName = node-sdk
sonar.projectVersion = 3.7.1
sonar.projectVersion = 3.7.2
sonar.exclusions=tests/**,examples/**,node_modules/**,coverage/**
sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.verbose = true
2 changes: 1 addition & 1 deletion src/request/request.builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class RequestBuilder {

// Merge provided query params with nonce and timestamp.
const queryString = buildQueryString(Object.assign(
this.queryParams || {},
this.queryParams,
{
nonce: uuid.v4(),
timestamp: Date.now(),
Expand Down
31 changes: 24 additions & 7 deletions src/request/request.handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,46 @@ const yotiCommon = require('../yoti_common');
* Default HTTP request handler.
*
* @param {YotiRequest} yotiRequest
* @param {boolean} buffer Return the response as a Buffer.
*
* @returns {Promise} Resolves {YotiResponse}
*/
module.exports.execute = yotiRequest => new Promise((resolve, reject) => {
module.exports.execute = (yotiRequest, buffer = false) => new Promise((resolve, reject) => {
const request = superagent(yotiRequest.getMethod(), yotiRequest.getUrl());

if (yotiCommon.requestCanSendPayload(yotiRequest.getMethod())) {
request.send(yotiRequest.getPayload().getPayloadJSON());
}

if (buffer === true) {
request.buffer(buffer);
}

if (yotiRequest.getHeaders()) {
request.set(yotiRequest.getHeaders());
}

request
.then((response) => {
try {
const parsedResponse = JSON.parse(response.text);
const receipt = parsedResponse.receipt || null;
return resolve(new YotiResponse(parsedResponse, response.statusCode, receipt));
} catch (err) {
return reject(err);
let parsedResponse = null;
let body = null;
let receipt = null;

if (response.body instanceof Buffer) {
body = response.body;
parsedResponse = response.body;
} else if (response.text) {
body = response.text;
parsedResponse = response.headers['content-type'] ? response.body : JSON.parse(response.text);
receipt = parsedResponse.receipt || null;
}

return resolve(new YotiResponse(
parsedResponse,
response.statusCode,
receipt,
body
));
})
.catch((err) => {
console.log(`Error getting data from Connect API: ${err.message}`);
Expand Down
6 changes: 4 additions & 2 deletions src/request/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,12 @@ class YotiRequest {
/**
* Executes the request.
*
* @param {boolean} buffer Return the response as a Buffer.
*
* @returns {Promise} Resolves {YotiResponse}
*/
execute() {
return requestHandler.execute(this);
execute(buffer = false) {
return requestHandler.execute(this, buffer);
}
}

Expand Down
11 changes: 10 additions & 1 deletion src/request/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ class YotiResponse {
* @param {*} parsedResponse
* @param {int} statusCode
* @param {Object|null} receipt
* @param {Buffer|string|null} body
*/
constructor(parsedResponse, statusCode, receipt = null) {
constructor(parsedResponse, statusCode, receipt = null, body = null) {
this.parsedResponse = parsedResponse;
this.statusCode = statusCode;
this.receipt = receipt;
this.body = body;
}

/**
Expand All @@ -29,6 +31,13 @@ class YotiResponse {
return this.parsedResponse;
}

/**
* @returns {Buffer|string|null} The response body.
*/
getBody() {
return this.body;
}

/**
* @returns {int} Response status code.
*/
Expand Down
24 changes: 24 additions & 0 deletions tests/request/request.builder.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,30 @@ describe('RequestBuilder', () => {
assertExpectedRequest(request, done);
});
});
describe('#withGet', () => {
it('should set method to GET', () => {
const request = new RequestBuilder()
.withBaseUrl(API_BASE_URL)
.withPemFilePath(PEM_FILE_PATH)
.withEndpoint(API_ENDPOINT)
.withGet()
.build();

expect(request.getMethod()).toBe('GET');
});
});
describe('#withPost', () => {
it('should set method to POST', () => {
const request = new RequestBuilder()
.withBaseUrl(API_BASE_URL)
.withPemFilePath(PEM_FILE_PATH)
.withEndpoint(API_ENDPOINT)
.withPost()
.build();

expect(request.getMethod()).toBe('POST');
});
});
describe('#withHeader', () => {
it('should only accept string header value', () => {
expect(() => {
Expand Down
176 changes: 176 additions & 0 deletions tests/request/request.handler.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
const nock = require('nock');
const fs = require('fs');
const yotiRequestHandler = require('../../src/request/request.handler');
const { RequestBuilder } = require('../../src/request/request.builder');
const { Payload } = require('../../src/request/payload');

const SOME_BASE_URL = 'https://someapi.yoti.com';
const SOME_ENDPOINT = '/some/endpoint';
const SOME_ENDPOINT_REG_EXP = new RegExp(`^${SOME_ENDPOINT}`);
const SOME_PEM_STRING = fs.readFileSync('./tests/sample-data/keys/node-sdk-test.pem', 'utf8');
const ALLOWED_METHODS = ['POST', 'PUT', 'PATCH', 'GET', 'DELETE'];
const SOME_JSON_DATA = { some: 'json' };
const SOME_JSON_DATA_STRING = JSON.stringify(SOME_JSON_DATA);
const SOME_JSON_RECEIPT_DATA = { receipt: 'some receipt' };
const SOME_JSON_RECEIPT_DATA_STRING = JSON.stringify(SOME_JSON_RECEIPT_DATA);
const SOME_DATA = 'someData';
const SOME_REQUEST = new RequestBuilder()
.withBaseUrl(SOME_BASE_URL)
.withEndpoint(SOME_ENDPOINT)
.withMethod('GET')
.withPemString(SOME_PEM_STRING)
.build();

/**
* @param {string} method
* @param {string} uri
* @param {integer} responseCode
* @param {string} body
*/
const mockResponse = (method, uri, responseCode, body, headers) => {
const scope = nock(SOME_BASE_URL);
const interceptor = scope[method.toLowerCase()](uri);
interceptor.reply(responseCode, body, headers);
};

describe('yotiRequest', () => {
afterEach((done) => {
nock.cleanAll();
done();
});

ALLOWED_METHODS.forEach((ALLOWED_METHOD) => {
describe(`when empty response is returned for ${ALLOWED_METHOD} method`, () => {
beforeEach((done) => {
mockResponse(ALLOWED_METHOD, SOME_ENDPOINT_REG_EXP, 200, '', {
'content-type': 'application/json',
});
done();
});

it('should return YotiResponse', (done) => {
const request = new RequestBuilder()
.withBaseUrl(SOME_BASE_URL)
.withEndpoint(SOME_ENDPOINT)
.withMethod(ALLOWED_METHOD)
.withPayload(new Payload(''))
.withPemString(SOME_PEM_STRING)
.build();

yotiRequestHandler
.execute(request)
.then((response) => {
expect(response.getParsedResponse()).toBeNull();
expect(response.getBody()).toBeNull();
expect(response.getStatusCode()).toBe(200);
done();
})
.catch(done);
});
});
describe(`when JSON response is returned for ${ALLOWED_METHOD} method`, () => {
beforeEach((done) => {
mockResponse(ALLOWED_METHOD, SOME_ENDPOINT_REG_EXP, 200, SOME_JSON_DATA_STRING, {
'content-type': 'application/json',
});
done();
});

it('should return YotiResponse', (done) => {
const request = new RequestBuilder()
.withBaseUrl(SOME_BASE_URL)
.withEndpoint(SOME_ENDPOINT)
.withMethod(ALLOWED_METHOD)
.withPayload(new Payload(''))
.withPemString(SOME_PEM_STRING)
.build();

yotiRequestHandler
.execute(request)
.then((response) => {
expect(response.getParsedResponse()).toStrictEqual(SOME_JSON_DATA);
expect(response.getBody()).toBe(SOME_JSON_DATA_STRING);
expect(response.getReceipt()).toBeNull();
expect(response.getStatusCode()).toBe(200);
done();
})
.catch(done);
});
});
});
describe('when receipt is returned', () => {
beforeEach((done) => {
nock(SOME_BASE_URL)
.get(SOME_ENDPOINT_REG_EXP)
.reply(200, SOME_JSON_RECEIPT_DATA_STRING, {
'content-type': 'application/json',
});
done();
});

it('should return YotiResponse', (done) => {
yotiRequestHandler
.execute(SOME_REQUEST)
.then((response) => {
expect(response.getParsedResponse()).toStrictEqual(SOME_JSON_RECEIPT_DATA);
expect(response.getBody()).toBe(SOME_JSON_RECEIPT_DATA_STRING);
expect(response.getReceipt()).toStrictEqual(SOME_JSON_RECEIPT_DATA.receipt);
expect(response.getStatusCode()).toBe(200);
done();
})
.catch(done);
});
});
[
'application/octet-stream',
'application/pdf',
'image/jpeg',
'image/png',
].forEach((mimeType) => {
describe(`when ${mimeType} content is returned`, () => {
beforeEach((done) => {
nock(SOME_BASE_URL)
.get(SOME_ENDPOINT_REG_EXP)
.reply(200, SOME_DATA, {
'Content-Type': mimeType,
});
done();
});
it('should return YotiResponse', (done) => {
yotiRequestHandler
.execute(SOME_REQUEST, true)
.then((response) => {
expect(response.getParsedResponse()).toBeInstanceOf(Buffer);
expect(response.getParsedResponse().toString()).toBe(SOME_DATA);
expect(response.getBody().toString()).toBe(SOME_DATA);
expect(response.getStatusCode()).toBe(200);
done();
})
.catch(done);
});
});
});
[
'text/plain',
].forEach((mimeType) => {
describe(`when ${mimeType} content is returned`, () => {
beforeEach((done) => {
nock(SOME_BASE_URL)
.get(SOME_ENDPOINT_REG_EXP)
.reply(200, SOME_DATA, {
'Content-Type': mimeType,
});
done();
});
it('should return YotiResponse', (done) => {
yotiRequestHandler
.execute(SOME_REQUEST)
.then((response) => {
expect(response.getBody()).toBe(SOME_DATA);
done();
})
.catch(done);
});
});
});
});
17 changes: 17 additions & 0 deletions tests/request/request.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const { YotiRequest } = require('../../src/request/request');
const requestHandler = require('../../src/request/request.handler');
const { Payload } = require('../../src/request/payload');

jest.mock('../../src/request/request.handler');

const SOME_URL = 'https://api.example.com/some-endpoint';
const SOME_METHOD = 'POST';
const SOME_PAYLOAD = new Payload('some payload');
Expand All @@ -10,6 +13,20 @@ const SOME_HEADERS = {
const SOME_REQUEST = new YotiRequest(SOME_METHOD, SOME_URL, SOME_HEADERS, SOME_PAYLOAD);

describe('YotiRequest', () => {
describe('#execute', () => {
it('should execute the request handler', () => {
SOME_REQUEST.execute();
expect(requestHandler.execute).toHaveBeenCalledWith(SOME_REQUEST, false);
});
it('should execute the request handler with buffer disabled', () => {
SOME_REQUEST.execute(false);
expect(requestHandler.execute).toHaveBeenCalledWith(SOME_REQUEST, false);
});
it('should execute the request handler with buffer enabled', () => {
SOME_REQUEST.execute(true);
expect(requestHandler.execute).toHaveBeenCalledWith(SOME_REQUEST, true);
});
});
describe('#getUrl', () => {
it('should return the URL', () => {
expect(SOME_REQUEST.getUrl()).toBe(SOME_URL);
Expand Down
Loading

0 comments on commit bd5316a

Please sign in to comment.