Skip to content

Commit

Permalink
Update handler to pass method & body requests + pass back response code
Browse files Browse the repository at this point in the history
- This will support future POST requests as well as support (e.g.) 404 responses

Minor test refactors:
- Set up new request defaults (method, empty body) + expected output & convert KibanaAuthHeader to string since they're set so close to one another
- group request pass tests into a describe block, remove response body portion of their tests (since we don't really care about that for those tests)
- group response tests into describe block
- clarify how passed handler params arg overrides request.params
  • Loading branch information
cee-chen committed Aug 27, 2020
1 parent 4d1f6f9 commit 74a4149
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ const fetchMock = require('node-fetch') as jest.Mock;
const { Response } = jest.requireActual('node-fetch');

const responseMock = {
ok: jest.fn(),
custom: jest.fn(),
customError: jest.fn(),
};
const KibanaAuthHeader = 'Basic 123';

describe('EnterpriseSearchRequestHandler', () => {
const enterpriseSearchRequestHandler = new EnterpriseSearchRequestHandler({
Expand Down Expand Up @@ -50,33 +49,66 @@ describe('EnterpriseSearchRequestHandler', () => {
});

EnterpriseSearchAPI.shouldHaveBeenCalledWith(
'http://localhost:3002/as/credentials/collection?type=indexed&pageIndex=1'
'http://localhost:3002/as/credentials/collection?type=indexed&pageIndex=1',
{ method: 'GET' }
);

expect(responseMock.ok).toHaveBeenCalledWith({
expect(responseMock.custom).toHaveBeenCalledWith({
body: responseBody,
statusCode: 200,
});
});

it('allows passing custom params', async () => {
const responseBody = {
results: [{ name: 'engine1' }],
meta: { page: { total_results: 1 } },
};
EnterpriseSearchAPI.mockReturn(responseBody);
describe('request passing', () => {
it('passes route method', async () => {
const requestHandler = enterpriseSearchRequestHandler.createRequest({ path: '/api/example' });

const requestHandler = enterpriseSearchRequestHandler.createRequest({
path: '/as/engines/collection',
params: '?some=custom&params=true',
await makeAPICall(requestHandler, { route: { method: 'POST' } });
EnterpriseSearchAPI.shouldHaveBeenCalledWith('http://localhost:3002/api/example', {
method: 'POST',
});

await makeAPICall(requestHandler, { route: { method: 'DELETE' } });
EnterpriseSearchAPI.shouldHaveBeenCalledWith('http://localhost:3002/api/example', {
method: 'DELETE',
});
});
await makeAPICall(requestHandler, { query: { overriden: true } });

EnterpriseSearchAPI.shouldHaveBeenCalledWith(
'http://localhost:3002/as/engines/collection?some=custom&params=true'
);
expect(responseMock.ok).toHaveBeenCalledWith({
body: responseBody,
it('passes request body', async () => {
const requestHandler = enterpriseSearchRequestHandler.createRequest({ path: '/api/example' });
await makeAPICall(requestHandler, { body: { bodacious: true } });

EnterpriseSearchAPI.shouldHaveBeenCalledWith('http://localhost:3002/api/example', {
body: '{"bodacious":true}',
});
});

it('passes custom params set by the handler, which override request params', async () => {
const requestHandler = enterpriseSearchRequestHandler.createRequest({
path: '/api/example',
params: '?some=custom&params=true',
});
await makeAPICall(requestHandler, { query: { overriden: true } });

EnterpriseSearchAPI.shouldHaveBeenCalledWith(
'http://localhost:3002/api/example?some=custom&params=true'
);
});
});

describe('response passing', () => {
it('returns the response status code from Enterprise Search', async () => {
EnterpriseSearchAPI.mockReturn({}, { status: 404 });

const requestHandler = enterpriseSearchRequestHandler.createRequest({ path: '/api/example' });
await makeAPICall(requestHandler);

EnterpriseSearchAPI.shouldHaveBeenCalledWith('http://localhost:3002/api/example');
expect(responseMock.custom).toHaveBeenCalledWith({ body: {}, statusCode: 404 });
});

// TODO: It's possible we may also pass back headers at some point
// from Enterprise Search, e.g. the x-read-only mode header
});

describe('error handling', () => {
Expand Down Expand Up @@ -136,14 +168,21 @@ describe('EnterpriseSearchRequestHandler', () => {
});

const makeAPICall = (handler: Function, params = {}) => {
const request = { headers: { authorization: KibanaAuthHeader }, ...params };
const request = {
headers: { authorization: 'Basic 123' },
route: { method: 'GET' },
body: {},
...params,
};
return handler(null, request, responseMock);
};

const EnterpriseSearchAPI = {
shouldHaveBeenCalledWith(expectedUrl: string, expectedParams = {}) {
expect(fetchMock).toHaveBeenCalledWith(expectedUrl, {
headers: { Authorization: KibanaAuthHeader },
headers: { Authorization: 'Basic 123' },
method: 'GET',
body: undefined,
...expectedParams,
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,31 @@ export class EnterpriseSearchRequestHandler {
response: KibanaResponseFactory
) => {
try {
// Set up API URL
params = params ?? (request.query ? `?${querystring.stringify(request.query)}` : '');
const url = encodeURI(this.enterpriseSearchUrl + path + params);

const apiResponse = await fetch(url, {
headers: { Authorization: request.headers.authorization as string },
});
// Set up API options
const { method } = request.route;
const headers = { Authorization: request.headers.authorization as string };
const body = Object.keys(request.body as object).length
? JSON.stringify(request.body)
: undefined;

// Call the Enterprise Search API and pass back response to the front-end
const apiResponse = await fetch(url, { method, headers, body });

if (apiResponse.url.endsWith('/login')) {
throw new Error('Cannot authenticate Enterprise Search user');
}

const body = await apiResponse.json();
const { status } = apiResponse;
const json = await apiResponse.json();

if (hasValidData(body)) {
return response.ok({ body });
if (hasValidData(json)) {
return response.custom({ statusCode: status, body: json });
} else {
this.log.debug(`Invalid data received from <${url}>: ${JSON.stringify(body)}`);
this.log.debug(`Invalid data received from <${url}>: ${JSON.stringify(json)}`);
throw new Error('Invalid data received');
}
} catch (e) {
Expand Down

0 comments on commit 74a4149

Please sign in to comment.