liveperson-functions-client
Version:
JavaScript client for LivePerson Functions.
331 lines • 13.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const jwt = __importStar(require("jsonwebtoken"));
const baseClient_1 = require("../../src/client/baseClient");
const client_1 = require("../../src/client/client");
const csdsClient_1 = require("../../src/helper/csdsClient");
const nock_1 = __importDefault(require("nock"));
const secret = 'mySecret';
const TEST_V2_HOST = 'fninvocations.test124.com'; // V2 hosts starts with fninvocations and is required to trigger v2 flow
const ACCOUNT_ID = '123456';
jest.mock('../../src/helper/csdsClient', () => {
return {
CsdsClient: jest.fn().mockImplementation(() => {
return {
get: jest.fn(() => TEST_V2_HOST),
};
}),
};
});
jest.mock('simple-oauth2', () => ({
ClientCredentials: jest.fn(() => ({
getToken: async () => ({
token: {
access_token: jwt.sign({
aud: 'le4711',
azp: 'bf16f923-b256-40c8-afa5-1b8e8372da09',
scope: 'faas.lambda.invoke',
iss: 'Sentinel',
exp: Date.now() / 1000 + 60 * 60,
iat: Date.now(),
}, secret),
},
}),
})),
}));
const testConfig = {
accountId: '123456',
authStrategy: {
clientId: 'foo',
clientSecret: 'bar',
},
};
describe('Client V2 flow', () => {
beforeEach(() => {
nock_1.default.cleanAll();
nock_1.default.enableNetConnect();
});
afterEach(jest.clearAllMocks);
describe('success flows', () => {
test('class and constructor - Base', () => {
const client = new client_1.Client(testConfig);
expect(csdsClient_1.CsdsClient).toHaveBeenCalledTimes(1);
expect(client).toBeInstanceOf(client_1.Client);
expect(client).toBeInstanceOf(baseClient_1.BaseClient);
});
test('invoke method', async () => {
const lpEventSource = 'testSystem';
const result1 = [123];
const result2 = [456];
const scope = (0, nock_1.default)(`https://${TEST_V2_HOST}`, {
reqheaders: {
'Content-Type': 'application/json',
'User-Agent': value => true,
'X-Request-ID': value => true,
Authorization: value => true,
'LP-EventSource': lpEventSource,
},
})
.post('/api/account/123456/events/fooBar/invoke')
.once()
.reply(202, result1)
.persist()
.post('/api/account/le12345/events/fooBar/invoke')
.once()
.reply(202, result2)
.persist();
const client1 = new client_1.Client(testConfig);
const response1 = await client1.invoke({
eventId: 'fooBar',
lpEventSource,
body: {
payload: {},
},
});
expect(response1).toBeNonEmptyObject();
expect(response1.body).toEqual(result1);
const client2 = new client_1.Client({ ...testConfig, accountId: 'le12345' });
const response2 = await client2.invoke({
eventId: 'fooBar',
externalSystem: 'testSystem',
body: {
payload: {},
},
});
expect(response2).toBeNonEmptyObject();
expect(response2.body).toEqual(result2);
expect(scope.isDone()).toBe(true);
});
test('invocation metrics', async () => {
const lpEventSource = 'testSystem';
let onInvokeCalled = false;
class MyMetricCollector {
onInvoke(invocationData) {
expect(invocationData.lpEventSource).toEqual(lpEventSource);
expect(invocationData.externalSystem).toBeUndefined();
onInvokeCalled = true;
return;
}
onGetLambdas(invocationData) {
return;
}
onIsImplemented(invocationData) {
return;
}
}
const result1 = [123];
const scope = (0, nock_1.default)(`https://${TEST_V2_HOST}`)
.post('/api/account/123456/events/fooBar/invoke')
.once()
.reply(202, result1)
.persist();
const client1 = new client_1.Client(testConfig, {
metricCollector: new MyMetricCollector(),
});
await client1.invoke({
eventId: 'fooBar',
lpEventSource,
body: {
payload: {},
},
});
expect(onInvokeCalled).toBe(true);
expect(scope.isDone()).toBe(true);
});
test('invocation metrics should contain deprecated externalSystem', async () => {
const lpEventSource = 'testSystem';
let onInvokeCalled = false;
class MyMetricCollector {
onInvoke(invocationData) {
expect(invocationData.lpEventSource).toEqual(lpEventSource);
expect(invocationData.externalSystem).toEqual(lpEventSource);
onInvokeCalled = true;
return;
}
onGetLambdas(invocationData) {
return;
}
onIsImplemented(invocationData) {
return;
}
}
const result1 = [123];
const scope = (0, nock_1.default)(`https://${TEST_V2_HOST}`)
.post('/api/account/123456/events/fooBar/invoke')
.once()
.reply(202, result1)
.persist();
const client1 = new client_1.Client(testConfig, {
metricCollector: new MyMetricCollector(),
});
await client1.invoke({
eventId: 'fooBar',
externalSystem: lpEventSource,
body: {
payload: {},
},
});
expect(onInvokeCalled).toBe(true);
expect(scope.isDone()).toBe(true);
});
test('getLambdas deprecated method to get V2 functions', async () => {
const lambdas = [{ uuid: 'a-b-c-d' }];
const scope = (0, nock_1.default)(`https://${TEST_V2_HOST}`)
.get('/api/account/123456/functions?eventId=&userId=&functionName=')
.once()
.reply(200, lambdas)
.persist();
const client = new client_1.Client({ ...testConfig, accountId: ACCOUNT_ID });
const response = await client.getLambdas({
externalSystem: 'testSystem',
});
expect(response).toBeNonEmptyObject();
expect(response.body).toEqual(lambdas);
expect(scope.isDone()).toBe(true);
});
test('getFunctions', async () => {
const lambdas = [{ uuid: 'a-b-c-d' }];
const scope = (0, nock_1.default)(`https://${TEST_V2_HOST}`)
.get('/api/account/123456/functions?eventId=&userId=&functionName=')
.once()
.reply(200, lambdas)
.persist();
const client = new client_1.Client({ ...testConfig, accountId: ACCOUNT_ID });
const response = await client.getFunctions({
externalSystem: 'testSystem',
});
expect(response).toBeNonEmptyObject();
expect(response.body).toEqual(lambdas);
expect(scope.isDone()).toBe(true);
});
test('should retry on receiving a network error', async () => {
const errorCode = { code: 'ECONNRESET' };
const scope = (0, nock_1.default)(`https://${TEST_V2_HOST}`)
.post('/api/account/123456/lambdas/this-is-a-uuid/invoke')
.times(3)
.replyWithError(errorCode);
const client = new client_1.Client(testConfig);
const response = await client.invoke({
lambdaUuid: 'this-is-a-uuid',
externalSystem: 'test-system',
body: {
payload: {},
},
});
expect(response.retryCount).toEqual(3);
expect(scope.isDone()).toBe(true);
});
});
describe('Unhappy flows', () => {
test('should throw if Functions returns a none-okay status code', async () => {
const scope = (0, nock_1.default)(`https://${TEST_V2_HOST}`)
.post('/api/account/123456/lambdas/this-is-a-uuid/invoke')
.times(3)
.reply(502)
.persist();
const config = { ...testConfig, failOnErrorStatusCode: true };
const client = new client_1.Client(config);
try {
await client.invoke({
lambdaUuid: 'this-is-a-uuid',
externalSystem: 'test-system',
body: {
payload: {},
},
});
}
catch (error) {
expect(error).toMatchObject({
name: 'FaaSInvokeError',
message: expect.stringContaining('502 - Bad Gateway'),
});
expect(scope.isDone()).toBe(true);
}
});
test('should map V2 errors code to V1 error', async () => {
var _a, _b, _c, _d;
const scope = (0, nock_1.default)(`https://${TEST_V2_HOST}`)
.post('/api/account/123456/lambdas/this-is-a-uuid/invoke')
.times(3)
.reply(901, {
code: "com.customer.faas.function.threw-error",
message: "something wrong"
})
.persist();
const config = { ...testConfig, failOnErrorStatusCode: true };
const client = new client_1.Client(config);
try {
await client.invoke({
lambdaUuid: 'this-is-a-uuid',
externalSystem: 'test-system',
v1CompError: true,
body: {
payload: {},
},
});
}
catch (error) {
expect(error).toMatchObject({
name: 'FaaSLambdaError',
message: expect.stringContaining('901'),
});
expect((_d = (_c = (_b = (_a = error === null || error === void 0 ? void 0 : error.jse_cause) === null || _a === void 0 ? void 0 : _a.jse_cause) === null || _b === void 0 ? void 0 : _b.jse_info) === null || _c === void 0 ? void 0 : _c.response) === null || _d === void 0 ? void 0 : _d.body).toMatchObject({
errorCode: 'com.liveperson.faas.handler.custom-failure',
errorMsg: "something wrong",
});
expect(scope.isDone()).toBe(true);
}
});
test('should throw if network errors are raised continuously', async () => {
const errorCode = { code: 'ECONNRESET' };
const scope = (0, nock_1.default)(`https://${TEST_V2_HOST}`)
.post('/api/account/123456/lambdas/this-is-a-uuid/invoke')
.times(3)
.replyWithError(errorCode);
const config = { ...testConfig, failOnErrorStatusCode: true };
const client = new client_1.Client(config);
try {
await client.invoke({
lambdaUuid: 'this-is-a-uuid',
externalSystem: 'test-system',
body: {
payload: {},
},
});
}
catch (error) {
expect(error).toMatchObject({
name: 'FaaSInvokeError',
});
expect(scope.isDone()).toBe(true);
}
});
});
});
//# sourceMappingURL=client.v2.test.js.map