diff --git a/aws/apigateway-aurora-lambda/README.md b/aws/apigateway-aurora-lambda/README.md
index afc4cc1..7ec279c 100644
--- a/aws/apigateway-aurora-lambda/README.md
+++ b/aws/apigateway-aurora-lambda/README.md
@@ -3,14 +3,13 @@
-
UUID
+
Offices and Employees
- Write a backend that exposes an endpoint that whenever invoked generates a new UUID, writes that to the database and returns it to the calling client. The backend should also expose an API that accepts a UUID and updates the updated_at timestamp if its already present in the db, else inserts it.
+ Write a multi-tenant backend for a SAAS ERP solution which allows storage/retrieval/manipulation of offices and employee data. Employees can work in multiple offices and each office can have multiple employees.
- ___
-
+---
@@ -27,19 +26,27 @@
- ___
+---
+
+We’re always looking for people who value their work, so come and join us. We are hiring!
- We’re always looking for people who value their work, so come and join us. We are hiring!
## Rest APIs
+
Use API Gateway to expose a backend that provides
-- a GET request that returns a new UUID when invoked and writes the value to a mysql variant of Aurora DB
-- a PUT request that accepts a UUID, updates the updated_at timestamp if its already present in the db, else inserts it.
+
+- a PUT request that writes the employee details to a mysql variant of Aurora DB
+- a PUT request that writes the office details to a mysql variant of Aurora DB
+- a GET request that queries the DB by employeeId and returns the employee info
+- a GET request that queries the DB by officeId and returns the office info
+- a PUT request that associates office with employee
+- a GET request that queries the DB and returns the all the offices or all the offices of a particular employee with pagination support
+- a GET request that queries the DB and returns the all the employees or all the employees of a particular office with pagination support
## Migrations
-Migrations are handled using sequelize. After a successful deployment a migration script is triggered by executing the following command:
+Migrations are handled using sequelize. After a successful deployment a migration script is triggered by executing the following command:
`sls migrations up --host= --stage=`
-The host URL is fetched by querying the cloudformation Output for the RDSHost.
+The host URL is fetched by querying the cloudformation Output for the RDSHost.
diff --git a/aws/apigateway-aurora-lambda/__mocks__/constants.js b/aws/apigateway-aurora-lambda/__mocks__/constants.js
index 580af64..0fa657a 100644
--- a/aws/apigateway-aurora-lambda/__mocks__/constants.js
+++ b/aws/apigateway-aurora-lambda/__mocks__/constants.js
@@ -5,5 +5,22 @@ MockDate.set('2000-11-22');
export const CONSTANTS = {
uuid: 'uuid',
- updatedAt: moment().format('YYYY-MM-DD HH:mm:s:ss')
+ updatedAt: moment().format('YYYY-MM-DD HH:mm:s:ss'),
+ employee: {
+ id: 1,
+ employee_name: 'Tapan',
+ office_id: null,
+ updated_at: '2020-10-25T16:02:21.000Z'
+ },
+ office: {
+ id: 1,
+ office_name: 'Wednesday Solutions',
+ office_address: 'Pune, MH, India',
+ employee_id: null,
+ updated_at: '2020-10-25T16:01:55.000Z'
+ }
};
+export const GET_EMP_BY_OFF_ID =
+ '{"res":{"employeeList":[{"employee_name":"Tapan","office_id":null,"updated_at":"2000-11-22 05:30:0:00","id":1,"createdAt":"2000-11-22T00:00:00.000Z","updatedAt":"2000-11-22T00:00:00.000Z"}],"office":{"office_name":"Wednesday Solutions","office_address":"Pune, MH, India","employee_id":null,"updated_at":"2000-11-22 05:30:0:00","id":1,"createdAt":"2000-11-22T00:00:00.000Z","updatedAt":"2000-11-22T00:00:00.000Z"}}}';
+export const GET_OFF_BY_EMP_ID =
+ '{"res":{"officeList":[{"office_name":"Wednesday Solutions","office_address":"Pune, MH, India","employee_id":null,"updated_at":"2000-11-22 05:30:0:00","id":1,"createdAt":"2000-11-22T00:00:00.000Z","updatedAt":"2000-11-22T00:00:00.000Z"}],"employee":{"employee_name":"Tapan","office_id":null,"updated_at":"2000-11-22 05:30:0:00","id":1,"createdAt":"2000-11-22T00:00:00.000Z","updatedAt":"2000-11-22T00:00:00.000Z"}}}';
diff --git a/aws/apigateway-aurora-lambda/functions/employee-office/join/data.json b/aws/apigateway-aurora-lambda/functions/employee-office/join/data.json
new file mode 100644
index 0000000..4b3a077
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee-office/join/data.json
@@ -0,0 +1,7 @@
+{
+ "body": "{\"employeeId\":1, \"officeId\":2}",
+ "headers": {
+ "content-type": "application/json",
+ "x-ws-system-id": "WS"
+ }
+}
diff --git a/aws/apigateway-aurora-lambda/functions/employee-office/join/index.js b/aws/apigateway-aurora-lambda/functions/employee-office/join/index.js
new file mode 100644
index 0000000..2026053
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee-office/join/index.js
@@ -0,0 +1,24 @@
+import { getDB } from '@models';
+import { success, failure, logHandler, getSystemId } from '@utils';
+export const handler = async (event, context, callback) =>
+ logHandler(event, async () => {
+ try {
+ const { employeeId, officeId } = JSON.parse(event.body);
+ if (!getSystemId(event)) {
+ throw new Error('Request Id Missing!');
+ }
+ const { employeeOffice } = getDB();
+
+ const res = await employeeOffice.create({
+ office_id: officeId,
+ employee_id: employeeId
+ });
+ return success(callback, {
+ status: 200,
+ body: JSON.stringify({ res })
+ });
+ } catch (err) {
+ console.log(err);
+ return failure(callback, err);
+ }
+ });
diff --git a/aws/apigateway-aurora-lambda/functions/employee-office/join/package.json b/aws/apigateway-aurora-lambda/functions/employee-office/join/package.json
new file mode 100644
index 0000000..ab8c871
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee-office/join/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "join",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/aws/apigateway-aurora-lambda/functions/employee-office/join/tests/index.test.js b/aws/apigateway-aurora-lambda/functions/employee-office/join/tests/index.test.js
new file mode 100644
index 0000000..7e7bcce
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee-office/join/tests/index.test.js
@@ -0,0 +1,37 @@
+import { resetAndMockDB } from '@utils/testUtils';
+
+describe('join-office-employee', () => {
+ let event;
+ let mocks;
+
+ beforeEach(() => {
+ event = require('../data.json');
+ mocks = {
+ callback: jest.fn()
+ };
+ jest.spyOn(mocks, 'callback');
+ });
+ it('should update/put an employee', async () => {
+ await resetAndMockDB(mockDbs => {
+ mockDbs.employeeOffice.create = () => true;
+ });
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
+ expect(mocks.callback.mock.calls[0][1]).toEqual({
+ status: 200,
+ body: JSON.stringify({ res: true })
+ });
+ });
+
+ it('should throw an error when req Id is missing', async () => {
+ event.headers['x-ws-system-id'] = null;
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toStrictEqual(`Request Id Missing!`);
+ expect(mocks.callback.mock.calls[0][1]).toBe(undefined);
+ });
+});
diff --git a/aws/apigateway-aurora-lambda/functions/employee/get-employee/data.json b/aws/apigateway-aurora-lambda/functions/employee/get-employee/data.json
new file mode 100644
index 0000000..7453f56
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/get-employee/data.json
@@ -0,0 +1,7 @@
+{
+ "body": "{\"employeeId\":\"1\"}",
+ "headers": {
+ "content-type": "application/json",
+ "x-ws-system-id": "WS"
+ }
+}
diff --git a/aws/apigateway-aurora-lambda/functions/employee/get-employee/index.js b/aws/apigateway-aurora-lambda/functions/employee/get-employee/index.js
new file mode 100644
index 0000000..74a1e9c
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/get-employee/index.js
@@ -0,0 +1,19 @@
+import { getDB } from '@models';
+import { success, failure, logHandler, getSystemId } from '@utils';
+export const handler = async (event, context, callback) =>
+ logHandler(event, async () => {
+ try {
+ const { employeeId } = JSON.parse(event.body);
+ if (!getSystemId(event)) {
+ throw new Error('Request Id Missing!');
+ }
+ const res = await getDB().employees.findOne({ where: { id: employeeId }, raw: true });
+ return success(callback, {
+ status: 200,
+ body: JSON.stringify({ res })
+ });
+ } catch (err) {
+ console.log(err);
+ return failure(callback, err);
+ }
+ });
diff --git a/aws/apigateway-aurora-lambda/functions/employee/get-employee/package.json b/aws/apigateway-aurora-lambda/functions/employee/get-employee/package.json
new file mode 100644
index 0000000..10ccf62
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/get-employee/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "getEmployee",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/aws/apigateway-aurora-lambda/functions/employee/get-employee/tests/index.test.js b/aws/apigateway-aurora-lambda/functions/employee/get-employee/tests/index.test.js
new file mode 100644
index 0000000..f87b585
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/get-employee/tests/index.test.js
@@ -0,0 +1,38 @@
+import { CONSTANTS } from '@mocks/constants';
+import { resetAndMockDB } from '@utils/testUtils';
+
+describe('get-employee', () => {
+ let event;
+ let mocks;
+
+ beforeEach(() => {
+ event = require('../data.json');
+ mocks = {
+ callback: jest.fn()
+ };
+ jest.spyOn(mocks, 'callback');
+ });
+ it('should fetch employee with employeeId', async () => {
+ await resetAndMockDB(mockDbs => {
+ mockDbs.employees.findOne = () => CONSTANTS.employee;
+ });
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
+ expect(mocks.callback.mock.calls[0][1]).toEqual({
+ status: 200,
+ body: JSON.stringify({ res: CONSTANTS.employee })
+ });
+ });
+
+ it('should throw an error when req Id is missing', async () => {
+ event.headers['x-ws-system-id'] = null;
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toStrictEqual(`Request Id Missing!`);
+ expect(mocks.callback.mock.calls[0][1]).toBe(undefined);
+ });
+});
diff --git a/aws/apigateway-aurora-lambda/functions/employee/get-employees/data.json b/aws/apigateway-aurora-lambda/functions/employee/get-employees/data.json
new file mode 100644
index 0000000..c7c0ac4
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/get-employees/data.json
@@ -0,0 +1,7 @@
+{
+ "body": "{\"officeId\":1}",
+ "headers": {
+ "content-type": "application/json",
+ "x-ws-system-id": "WS"
+ }
+}
diff --git a/aws/apigateway-aurora-lambda/functions/employee/get-employees/index.js b/aws/apigateway-aurora-lambda/functions/employee/get-employees/index.js
new file mode 100644
index 0000000..4669df0
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/get-employees/index.js
@@ -0,0 +1,36 @@
+import { getDB } from '@models';
+import { success, failure, logHandler, getSystemId } from '@utils';
+export const handler = async (event, context, callback) =>
+ logHandler(event, async () => {
+ try {
+ const { limit, offset, officeId } = JSON.parse(event.body);
+ if (!getSystemId(event)) {
+ throw new Error('Request Id Missing!');
+ }
+ let res;
+ const employeeList = [];
+
+ const { offices, employeeOffice, employees } = getDB();
+ if (!officeId) {
+ res = await employees.findAll();
+ } else {
+ const employeeOfficeRes = await employeeOffice.findAll({ limit, offset, where: { office_id: officeId } });
+ await Promise.all(
+ employeeOfficeRes.map(async e => {
+ employeeList.push(await employees.findOne({ where: { id: e.dataValues.employee_id }, raw: true }));
+ })
+ );
+ res = {
+ employeeList,
+ office: await offices.findOne({ where: { id: officeId }, raw: true })
+ };
+ }
+ return success(callback, {
+ status: 200,
+ body: JSON.stringify({ res })
+ });
+ } catch (err) {
+ console.log(err);
+ return failure(callback, err);
+ }
+ });
diff --git a/aws/apigateway-aurora-lambda/functions/employee/get-employees/package.json b/aws/apigateway-aurora-lambda/functions/employee/get-employees/package.json
new file mode 100644
index 0000000..ed014a2
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/get-employees/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "get-employees",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/aws/apigateway-aurora-lambda/functions/employee/get-employees/tests/index.test.js b/aws/apigateway-aurora-lambda/functions/employee/get-employees/tests/index.test.js
new file mode 100644
index 0000000..fe33f24
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/get-employees/tests/index.test.js
@@ -0,0 +1,54 @@
+import { CONSTANTS, GET_EMP_BY_OFF_ID } from '@mocks/constants';
+import { resetAndMockDB } from '@utils/testUtils';
+
+describe('get-employees', () => {
+ let event;
+ let mocks;
+
+ beforeEach(() => {
+ event = require('../data.json');
+ mocks = {
+ callback: jest.fn()
+ };
+ jest.spyOn(mocks, 'callback');
+ });
+ it('should fetch all employees', async () => {
+ await resetAndMockDB(mockDbs => {
+ mockDbs.employees.findAll = () => [CONSTANTS.employee];
+ });
+ const handler = require('../index').handler;
+ event.body = '{}';
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
+ expect(mocks.callback.mock.calls[0][1]).toEqual({
+ status: 200,
+ body: JSON.stringify({ res: [CONSTANTS.employee] })
+ });
+ });
+
+ it('should fetch all employees of the given officeId', async () => {
+ await resetAndMockDB(mockDbs => {
+ mockDbs.employeeOffice.findAll = () => [{ dataValues: CONSTANTS.employee }];
+ });
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
+ expect(mocks.callback.mock.calls[0][1]).toEqual({
+ status: 200,
+ body: GET_EMP_BY_OFF_ID
+ });
+ });
+
+ it('should throw an error when req Id is missing', async () => {
+ event.headers['x-ws-system-id'] = null;
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toStrictEqual(`Request Id Missing!`);
+ expect(mocks.callback.mock.calls[0][1]).toBe(undefined);
+ });
+});
diff --git a/aws/apigateway-aurora-lambda/functions/employee/put-employee/data.json b/aws/apigateway-aurora-lambda/functions/employee/put-employee/data.json
new file mode 100644
index 0000000..677b2b4
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/put-employee/data.json
@@ -0,0 +1,7 @@
+{
+ "body": "{\"employeeName\":\"Mac\"}",
+ "headers": {
+ "content-type": "application/json",
+ "x-ws-system-id": "WS"
+ }
+}
diff --git a/aws/apigateway-aurora-lambda/functions/employee/put-employee/index.js b/aws/apigateway-aurora-lambda/functions/employee/put-employee/index.js
new file mode 100644
index 0000000..bd1d185
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/put-employee/index.js
@@ -0,0 +1,20 @@
+import { getDB } from '@models';
+import { success, failure, logHandler, getSystemId } from '@utils';
+export const handler = async (event, context, callback) =>
+ logHandler(event, async () => {
+ try {
+ const { employeeName, officeId } = JSON.parse(event.body);
+ if (!getSystemId(event)) {
+ throw new Error('Request Id Missing!');
+ }
+ const res = await getDB().employees.upsert({ employee_name: employeeName, office_id: officeId });
+
+ return success(callback, {
+ status: 200,
+ body: JSON.stringify({ res })
+ });
+ } catch (err) {
+ console.log(err);
+ return failure(callback, err);
+ }
+ });
diff --git a/aws/apigateway-aurora-lambda/functions/employee/put-employee/package.json b/aws/apigateway-aurora-lambda/functions/employee/put-employee/package.json
new file mode 100644
index 0000000..728ba78
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/put-employee/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "putEmployee",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/aws/apigateway-aurora-lambda/functions/employee/put-employee/tests/index.test.js b/aws/apigateway-aurora-lambda/functions/employee/put-employee/tests/index.test.js
new file mode 100644
index 0000000..0c5d0f4
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/employee/put-employee/tests/index.test.js
@@ -0,0 +1,37 @@
+import { resetAndMockDB } from '@utils/testUtils';
+
+describe('put-employee', () => {
+ let event;
+ let mocks;
+
+ beforeEach(() => {
+ event = require('../data.json');
+ mocks = {
+ callback: jest.fn()
+ };
+ jest.spyOn(mocks, 'callback');
+ });
+ it('should update/put an employee', async () => {
+ await resetAndMockDB(mockDbs => {
+ mockDbs.employees.upsert = () => true;
+ });
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
+ expect(mocks.callback.mock.calls[0][1]).toEqual({
+ status: 200,
+ body: JSON.stringify({ res: true })
+ });
+ });
+
+ it('should throw an error when req Id is missing', async () => {
+ event.headers['x-ws-system-id'] = null;
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toStrictEqual(`Request Id Missing!`);
+ expect(mocks.callback.mock.calls[0][1]).toBe(undefined);
+ });
+});
diff --git a/aws/apigateway-aurora-lambda/functions/office/get-office/data.json b/aws/apigateway-aurora-lambda/functions/office/get-office/data.json
new file mode 100644
index 0000000..6ae18df
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/get-office/data.json
@@ -0,0 +1,7 @@
+{
+ "body": "{\"officeId\":\"1\"}",
+ "headers": {
+ "content-type": "application/json",
+ "x-ws-system-id": "WS"
+ }
+}
diff --git a/aws/apigateway-aurora-lambda/functions/office/get-office/index.js b/aws/apigateway-aurora-lambda/functions/office/get-office/index.js
new file mode 100644
index 0000000..1402434
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/get-office/index.js
@@ -0,0 +1,19 @@
+import { getDB } from '@models';
+import { success, failure, logHandler, getSystemId } from '@utils';
+export const handler = async (event, context, callback) =>
+ logHandler(event, async () => {
+ try {
+ const { officeId } = JSON.parse(event.body);
+ if (!getSystemId(event)) {
+ throw new Error('Request Id Missing!');
+ }
+ const res = await getDB().offices.findOne({ where: { id: officeId }, raw: true });
+ return success(callback, {
+ status: 200,
+ body: JSON.stringify({ res })
+ });
+ } catch (err) {
+ console.log(err);
+ return failure(callback, err);
+ }
+ });
diff --git a/aws/apigateway-aurora-lambda/functions/office/get-office/package.json b/aws/apigateway-aurora-lambda/functions/office/get-office/package.json
new file mode 100644
index 0000000..5f81265
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/get-office/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "get-office",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/aws/apigateway-aurora-lambda/functions/office/get-office/tests/index.test.js b/aws/apigateway-aurora-lambda/functions/office/get-office/tests/index.test.js
new file mode 100644
index 0000000..c0f60f8
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/get-office/tests/index.test.js
@@ -0,0 +1,38 @@
+import { CONSTANTS } from '@mocks/constants';
+import { resetAndMockDB } from '@utils/testUtils';
+
+describe('get-office', () => {
+ let event;
+ let mocks;
+
+ beforeEach(() => {
+ event = require('../data.json');
+ mocks = {
+ callback: jest.fn()
+ };
+ jest.spyOn(mocks, 'callback');
+ });
+ it('should fetch office with officeId', async () => {
+ await resetAndMockDB(mockDbs => {
+ mockDbs.offices.findOne = () => CONSTANTS.office;
+ });
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
+ expect(mocks.callback.mock.calls[0][1]).toEqual({
+ status: 200,
+ body: JSON.stringify({ res: CONSTANTS.office })
+ });
+ });
+
+ it('should throw an error when req Id is missing', async () => {
+ event.headers['x-ws-system-id'] = null;
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toStrictEqual(`Request Id Missing!`);
+ expect(mocks.callback.mock.calls[0][1]).toBe(undefined);
+ });
+});
diff --git a/aws/apigateway-aurora-lambda/functions/office/get-offices/data.json b/aws/apigateway-aurora-lambda/functions/office/get-offices/data.json
new file mode 100644
index 0000000..e1ac41b
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/get-offices/data.json
@@ -0,0 +1,7 @@
+{
+ "body": "{\"employeeId\":1}",
+ "headers": {
+ "content-type": "application/json",
+ "x-ws-system-id": "WS"
+ }
+}
diff --git a/aws/apigateway-aurora-lambda/functions/office/get-offices/index.js b/aws/apigateway-aurora-lambda/functions/office/get-offices/index.js
new file mode 100644
index 0000000..1c42ee5
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/get-offices/index.js
@@ -0,0 +1,36 @@
+import { getDB } from '@models';
+import { success, failure, logHandler, getSystemId } from '@utils';
+export const handler = async (event, context, callback) =>
+ logHandler(event, async () => {
+ try {
+ const { limit, offset, employeeId } = JSON.parse(event.body);
+ if (!getSystemId(event)) {
+ throw new Error('Request Id Missing!');
+ }
+ let res;
+ const officeList = [];
+
+ const { offices, employeeOffice, employees } = getDB();
+ if (!employeeId) {
+ res = await offices.findAll();
+ } else {
+ const employeeOfficeRes = await employeeOffice.findAll({ limit, offset, where: { employee_id: employeeId } });
+ await Promise.all(
+ employeeOfficeRes.map(async e => {
+ officeList.push(await offices.findOne({ where: { id: e.dataValues.office_id }, raw: true }));
+ })
+ );
+ res = {
+ officeList,
+ employee: await employees.findOne({ where: { id: employeeId }, raw: true })
+ };
+ }
+ return success(callback, {
+ status: 200,
+ body: JSON.stringify({ res })
+ });
+ } catch (err) {
+ console.log(err);
+ return failure(callback, err);
+ }
+ });
diff --git a/aws/apigateway-aurora-lambda/functions/office/get-offices/package.json b/aws/apigateway-aurora-lambda/functions/office/get-offices/package.json
new file mode 100644
index 0000000..ab69a02
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/get-offices/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "get-offices",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/aws/apigateway-aurora-lambda/functions/office/get-offices/tests/index.test.js b/aws/apigateway-aurora-lambda/functions/office/get-offices/tests/index.test.js
new file mode 100644
index 0000000..ebef0eb
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/get-offices/tests/index.test.js
@@ -0,0 +1,54 @@
+import { CONSTANTS, GET_OFF_BY_EMP_ID } from '@mocks/constants';
+import { resetAndMockDB } from '@utils/testUtils';
+
+describe('get-offices', () => {
+ let event;
+ let mocks;
+
+ beforeEach(() => {
+ event = require('../data.json');
+ mocks = {
+ callback: jest.fn()
+ };
+ jest.spyOn(mocks, 'callback');
+ });
+ it('should fetch all offices', async () => {
+ await resetAndMockDB(mockDbs => {
+ mockDbs.offices.findAll = () => [CONSTANTS.office];
+ });
+ const handler = require('../index').handler;
+ event.body = '{}';
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
+ expect(mocks.callback.mock.calls[0][1]).toEqual({
+ status: 200,
+ body: JSON.stringify({ res: [CONSTANTS.office] })
+ });
+ });
+
+ it('should fetch all offices of the given employeeId', async () => {
+ await resetAndMockDB(mockDbs => {
+ mockDbs.employeeOffice.findAll = () => [{ dataValues: CONSTANTS.office }];
+ });
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
+ expect(mocks.callback.mock.calls[0][1]).toEqual({
+ status: 200,
+ body: GET_OFF_BY_EMP_ID
+ });
+ });
+
+ it('should throw an error when req Id is missing', async () => {
+ event.headers['x-ws-system-id'] = null;
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toStrictEqual(`Request Id Missing!`);
+ expect(mocks.callback.mock.calls[0][1]).toBe(undefined);
+ });
+});
diff --git a/aws/apigateway-aurora-lambda/functions/office/put-office/data.json b/aws/apigateway-aurora-lambda/functions/office/put-office/data.json
new file mode 100644
index 0000000..3bd68ab
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/put-office/data.json
@@ -0,0 +1,7 @@
+{
+ "body": "{\"officeName\":\"WS\", \"officeAddress\":\"Pune, MH, India\"}",
+ "headers": {
+ "content-type": "application/json",
+ "x-ws-system-id": "WS"
+ }
+}
diff --git a/aws/apigateway-aurora-lambda/functions/office/put-office/index.js b/aws/apigateway-aurora-lambda/functions/office/put-office/index.js
new file mode 100644
index 0000000..70f3092
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/put-office/index.js
@@ -0,0 +1,24 @@
+import { getDB } from '@models';
+import { success, failure, logHandler, getSystemId } from '@utils';
+export const handler = async (event, context, callback) =>
+ logHandler(event, async () => {
+ try {
+ const { officeName, officeAddress, employeeId } = JSON.parse(event.body);
+ if (!getSystemId(event)) {
+ throw new Error('Request Id Missing!');
+ }
+ const res = await getDB().offices.upsert({
+ office_name: officeName,
+ office_address: officeAddress,
+ employee_id: employeeId
+ });
+
+ return success(callback, {
+ status: 200,
+ body: JSON.stringify({ res })
+ });
+ } catch (err) {
+ console.log(err);
+ return failure(callback, err);
+ }
+ });
diff --git a/aws/apigateway-aurora-lambda/functions/office/put-office/package.json b/aws/apigateway-aurora-lambda/functions/office/put-office/package.json
new file mode 100644
index 0000000..728ba78
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/put-office/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "putEmployee",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/aws/apigateway-aurora-lambda/functions/office/put-office/tests/index.test.js b/aws/apigateway-aurora-lambda/functions/office/put-office/tests/index.test.js
new file mode 100644
index 0000000..4f42413
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/office/put-office/tests/index.test.js
@@ -0,0 +1,37 @@
+import { resetAndMockDB } from '@utils/testUtils';
+
+describe('put-office', () => {
+ let event;
+ let mocks;
+
+ beforeEach(() => {
+ event = require('../data.json');
+ mocks = {
+ callback: jest.fn()
+ };
+ jest.spyOn(mocks, 'callback');
+ });
+ it('should update/put an office', async () => {
+ await resetAndMockDB(mockDbs => {
+ mockDbs.offices.upsert = () => true;
+ });
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
+ expect(mocks.callback.mock.calls[0][1]).toEqual({
+ status: 200,
+ body: JSON.stringify({ res: true })
+ });
+ });
+
+ it('should throw an error when req Id is missing', async () => {
+ event.headers['x-ws-system-id'] = null;
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toStrictEqual(`Request Id Missing!`);
+ expect(mocks.callback.mock.calls[0][1]).toBe(undefined);
+ });
+});
diff --git a/aws/apigateway-aurora-lambda/functions/uuid/get/tests/index.test.js b/aws/apigateway-aurora-lambda/functions/uuid/get/tests/index.test.js
index f2fbbcf..70b8001 100644
--- a/aws/apigateway-aurora-lambda/functions/uuid/get/tests/index.test.js
+++ b/aws/apigateway-aurora-lambda/functions/uuid/get/tests/index.test.js
@@ -4,7 +4,6 @@ describe('get-uuid', () => {
it('should get a new UUID', async () => {
const response = await handler();
const body = JSON.parse(response.body);
- console.log({ body });
expect(body.uuid).toEqual(CONSTANTS.uuid);
});
});
diff --git a/aws/apigateway-aurora-lambda/functions/uuid/put/data.json b/aws/apigateway-aurora-lambda/functions/uuid/put/data.json
new file mode 100644
index 0000000..ebcec2c
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/functions/uuid/put/data.json
@@ -0,0 +1,7 @@
+{
+ "body": "{\"uuid\":\"bf1cb7b6-6199-488a-a485-8b72e74c8c95\"}",
+ "headers": {
+ "content-type": "application/json",
+ "x-ws-system-id": "WS"
+ }
+}
diff --git a/aws/apigateway-aurora-lambda/functions/uuid/put/index.js b/aws/apigateway-aurora-lambda/functions/uuid/put/index.js
index a553b52..fd2685e 100644
--- a/aws/apigateway-aurora-lambda/functions/uuid/put/index.js
+++ b/aws/apigateway-aurora-lambda/functions/uuid/put/index.js
@@ -1,15 +1,29 @@
import moment from 'moment';
import { getDB } from '@models';
+import { success, failure, logHandler, getSystemId } from '@utils';
-export const handler = async event => {
- const { uuid } = JSON.parse(event.body);
- const dbResponse = await getDB().uuids.findOne({ where: { uuid }, raw: true });
- let newUUID;
- if (dbResponse) {
- await getDB().uuids.update({ updatedAt: moment() }, { where: { id: dbResponse.id } });
- newUUID = await getDB().uuids.findOne({ where: { uuid }, raw: true });
- } else {
- newUUID = await getDB().uuids.create({ uuid });
- }
- return { status: 200, body: JSON.stringify({ uuid: newUUID.uuid, updated_at: newUUID.updatedAt }) };
-};
+export const handler = async (event, context, callback) =>
+ logHandler(event, async () => {
+ try {
+ if (!getSystemId(event)) {
+ throw new Error('Request Id Missing!');
+ }
+ const { uuid } = JSON.parse(event.body);
+ const dbResponse = await getDB().uuids.findOne({ where: { uuid }, raw: true });
+ let newUUID;
+ const { uuids } = getDB();
+ if (dbResponse) {
+ await uuids.update({ updatedAt: moment() }, { where: { id: dbResponse.id } });
+ newUUID = await uuids.findOne({ where: { uuid }, raw: true });
+ } else {
+ newUUID = await uuids.create({ uuid });
+ }
+ return success(callback, {
+ status: 200,
+ body: JSON.stringify({ uuid: newUUID.uuid, updated_at: newUUID.updatedAt })
+ });
+ } catch (err) {
+ console.log(err);
+ return failure(callback, err);
+ }
+ });
diff --git a/aws/apigateway-aurora-lambda/functions/uuid/put/tests/index.test.js b/aws/apigateway-aurora-lambda/functions/uuid/put/tests/index.test.js
index 4a70e94..60bc634 100644
--- a/aws/apigateway-aurora-lambda/functions/uuid/put/tests/index.test.js
+++ b/aws/apigateway-aurora-lambda/functions/uuid/put/tests/index.test.js
@@ -2,13 +2,24 @@ import { CONSTANTS } from '@mocks/constants';
import { resetAndMockDB } from '@utils/testUtils';
describe('put-uuid', () => {
+ let mocks;
+ let event;
+
+ beforeEach(() => {
+ event = require('../data.json');
+
+ mocks = {
+ callback: jest.fn()
+ };
+ jest.spyOn(mocks, 'callback');
+ });
it('should update the updatedAt if the UUID is already present', async () => {
resetAndMockDB();
const handler = require('../index').handler;
- const response = await handler({ body: `{"uuid": "${CONSTANTS.uuid}"}` });
- const body = JSON.parse(response.body);
- expect(body.uuid).toEqual(CONSTANTS.uuid);
- expect(body.updated_at).toEqual('2000-11-22T00:00:00.000Z');
+ await handler({ body: `{"uuid": "${CONSTANTS.uuid}"}`, headers: event.headers }, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
});
it('should add a new UUID', async () => {
@@ -18,9 +29,17 @@ describe('put-uuid', () => {
mockDbs.uuids.create = () => ({ uuid: newUUID, updatedAt: CONSTANTS.updatedAt });
});
const handler = require('../index').handler;
- const response = await handler({ body: `{"uuid": "${CONSTANTS.uuid}"}` });
- const body = JSON.parse(response.body);
- expect(body.uuid).toEqual(newUUID);
- expect(body.updated_at).toEqual(CONSTANTS.updatedAt);
+ await handler({ body: `{"uuid": "${CONSTANTS.uuid}"}`, headers: event.headers }, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBeTruthy();
+ });
+ it('should throw an error when req Id is missing', async () => {
+ event.headers['x-ws-system-id'] = null;
+ const handler = require('../index').handler;
+ await handler(event, null, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ expect(mocks.callback.mock.calls[0][0]).toStrictEqual(`Request Id Missing!`);
+ expect(mocks.callback.mock.calls[0][1]).toBe(undefined);
});
});
diff --git a/aws/apigateway-aurora-lambda/migrations/resources/v1/02_employees.sql b/aws/apigateway-aurora-lambda/migrations/resources/v1/02_employees.sql
new file mode 100644
index 0000000..8bd1ce1
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/migrations/resources/v1/02_employees.sql
@@ -0,0 +1,6 @@
+create table employees (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ employee_name VARCHAR(36) UNIQUE NOT NULL,
+ office_id INT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
\ No newline at end of file
diff --git a/aws/apigateway-aurora-lambda/migrations/resources/v1/03_office.sql b/aws/apigateway-aurora-lambda/migrations/resources/v1/03_office.sql
new file mode 100644
index 0000000..c7b0c65
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/migrations/resources/v1/03_office.sql
@@ -0,0 +1,8 @@
+create table offices (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ office_name VARCHAR(36) UNIQUE NOT NULL,
+ office_address VARCHAR(36) NOT NULL,
+ employee_id INT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CONSTRAINT offices_employee_id_fk FOREIGN KEY (employee_id) REFERENCES employees (id)
+);
\ No newline at end of file
diff --git a/aws/apigateway-aurora-lambda/migrations/resources/v1/04_employee_office.sql b/aws/apigateway-aurora-lambda/migrations/resources/v1/04_employee_office.sql
new file mode 100644
index 0000000..ee4ef0c
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/migrations/resources/v1/04_employee_office.sql
@@ -0,0 +1,9 @@
+create table employee_office (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ employee_id INT NOT NULL,
+ office_id INT NOT NULL,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ CONSTRAINT employee_office_id UNIQUE (id),
+ CONSTRAINT employee_id_fk FOREIGN KEY (employee_id) REFERENCES employees (id),
+ CONSTRAINT office_id_fk FOREIGN KEY (office_id) REFERENCES offices (id)
+);
\ No newline at end of file
diff --git a/aws/apigateway-aurora-lambda/migrations/resources/v1/alter_employees.sql b/aws/apigateway-aurora-lambda/migrations/resources/v1/alter_employees.sql
new file mode 100644
index 0000000..7b854df
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/migrations/resources/v1/alter_employees.sql
@@ -0,0 +1 @@
+alter table employees add foreign key (office_id) references offices(id);
diff --git a/aws/apigateway-aurora-lambda/models/employee_office.js b/aws/apigateway-aurora-lambda/models/employee_office.js
new file mode 100644
index 0000000..0ef8025
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/models/employee_office.js
@@ -0,0 +1,44 @@
+/* jshint indent: 2 */
+
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define(
+ 'employee_office',
+ {
+ id: {
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true
+ },
+ employee_id: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ references: {
+ model: 'employees',
+ key: 'id'
+ },
+ unique: 'employee_id_fk'
+ },
+ office_id: {
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ references: {
+ model: 'offices',
+ key: 'id'
+ },
+ unique: 'office_id_fk'
+ },
+ updated_at: {
+ type: DataTypes.DATE,
+ allowNull: true,
+ defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
+ }
+ },
+ {
+ sequelize,
+ tableName: 'employee_office',
+ timestamps: false
+ }
+ );
+};
diff --git a/aws/apigateway-aurora-lambda/models/employees.js b/aws/apigateway-aurora-lambda/models/employees.js
new file mode 100644
index 0000000..39c469a
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/models/employees.js
@@ -0,0 +1,40 @@
+/* jshint indent: 2 */
+
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define(
+ 'employees',
+ {
+ id: {
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true
+ },
+ employee_name: {
+ type: DataTypes.STRING(36),
+ allowNull: false,
+ unique: 'employee_name'
+ },
+ office_id: {
+ type: DataTypes.INTEGER,
+ allowNull: true,
+ references: {
+ model: 'offices',
+ key: 'id'
+ },
+ unique: 'employees_ibfk_1'
+ },
+ updated_at: {
+ type: DataTypes.DATE,
+ allowNull: true,
+ defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
+ }
+ },
+ {
+ sequelize,
+ tableName: 'employees',
+ timestamps: false
+ }
+ );
+};
diff --git a/aws/apigateway-aurora-lambda/models/index.js b/aws/apigateway-aurora-lambda/models/index.js
index 04ca42d..f024515 100644
--- a/aws/apigateway-aurora-lambda/models/index.js
+++ b/aws/apigateway-aurora-lambda/models/index.js
@@ -28,8 +28,14 @@ function getDB() {
);
}
const uuids = require('@models/uuids');
+ const employees = require('@models/employees');
+ const offices = require('@models/offices');
+ const employeeOffice = require('@models/employee_office');
db.uuids = uuids(sequelize, Sequelize.DataTypes);
+ db.employees = employees(sequelize, Sequelize.DataTypes);
+ db.offices = offices(sequelize, Sequelize.DataTypes);
+ db.employeeOffice = employeeOffice(sequelize, Sequelize.DataTypes);
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
diff --git a/aws/apigateway-aurora-lambda/models/offices.js b/aws/apigateway-aurora-lambda/models/offices.js
new file mode 100644
index 0000000..f8de38e
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/models/offices.js
@@ -0,0 +1,44 @@
+/* jshint indent: 2 */
+
+const Sequelize = require('sequelize');
+module.exports = function(sequelize, DataTypes) {
+ return sequelize.define(
+ 'offices',
+ {
+ id: {
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
+ primaryKey: true
+ },
+ office_name: {
+ type: DataTypes.STRING(36),
+ allowNull: false,
+ unique: 'office_name'
+ },
+ office_address: {
+ type: DataTypes.STRING(36),
+ allowNull: false
+ },
+ employee_id: {
+ type: DataTypes.INTEGER,
+ allowNull: true,
+ references: {
+ model: 'employees',
+ key: 'id'
+ },
+ unique: 'offices_employee_id_fk'
+ },
+ updated_at: {
+ type: DataTypes.DATE,
+ allowNull: true,
+ defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
+ }
+ },
+ {
+ sequelize,
+ tableName: 'offices',
+ timestamps: false
+ }
+ );
+};
diff --git a/aws/apigateway-aurora-lambda/models/uuids.js b/aws/apigateway-aurora-lambda/models/uuids.js
index 4da659f..265eb72 100644
--- a/aws/apigateway-aurora-lambda/models/uuids.js
+++ b/aws/apigateway-aurora-lambda/models/uuids.js
@@ -1,26 +1,31 @@
-const moment = require('moment');
+/* jshint indent: 2 */
+
+const Sequelize = require('sequelize');
module.exports = function(sequelize, DataTypes) {
return sequelize.define(
'uuids',
{
id: {
- type: DataTypes.INTEGER(11),
- allowNull: true,
+ autoIncrement: true,
+ type: DataTypes.INTEGER,
+ allowNull: false,
primaryKey: true
},
uuid: {
type: DataTypes.STRING(36),
- allowNull: false
+ allowNull: false,
+ unique: 'uuid'
},
- updatedAt: {
- field: 'updated_at',
+ updated_at: {
type: DataTypes.DATE,
allowNull: true,
- defaultValue: moment().format('YYYY-MM-DD HH:mm:s:ss')
+ defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}
},
{
- tableName: 'uuids'
+ sequelize,
+ tableName: 'uuids',
+ timestamps: false
}
);
};
diff --git a/aws/apigateway-aurora-lambda/serverless.yml b/aws/apigateway-aurora-lambda/serverless.yml
index 0422bbd..1e57877 100644
--- a/aws/apigateway-aurora-lambda/serverless.yml
+++ b/aws/apigateway-aurora-lambda/serverless.yml
@@ -31,6 +31,45 @@ functions:
role: LambdaRDSRole
events:
- http: PUT uuids
+ getEmployees:
+ handler: functions/employee/get-employees/index.handler
+ role: LambdaDDBRole
+ events:
+ - http: GET employees
+
+ getEmployee:
+ handler: functions/employee/get-employee/index.handler
+ role: LambdaDDBRole
+ events:
+ - http: GET employee
+
+ getOffices:
+ handler: functions/office/get-offices/index.handler
+ role: LambdaDDBRole
+
+ getOffice:
+ handler: functions/office/get-office/index.handler
+ role: LambdaDDBRole
+ events:
+ - http: GET office
+
+ putEmployee:
+ handler: functions/employee/put-employee/index.handler
+ role: LambdaDDBRole
+ events:
+ - http: PUT employee
+
+ putOffice:
+ handler: functions/office/put-office/index.handler
+ role: LambdaDDBRole
+ events:
+ - http: PUT office
+ joinEmployeeAndOffice:
+ handler: functions/employee-office/join/index.handler
+ role: LambdaDDBRole
+ events:
+ - http: PUT join-employee-office
+
custom:
scripts:
hooks:
@@ -41,14 +80,14 @@ custom:
DB_NAME: uuids_${env:RDS_PREFIX}_${env:STAGE}
resources:
Resources:
-# internet gateway
+ # internet gateway
ServerlessInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: 'Name'
Value: 'ServerlessInternetGateway'
-# route table
+ # route table
RouteTablePublic:
Type: AWS::EC2::RouteTable
Properties:
@@ -65,7 +104,7 @@ resources:
Ref: ServerlessInternetGateway
RouteTableId:
Ref: RouteTablePublic
-#route table associations
+ #route table associations
RouteTableAssociationSubnetA:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
@@ -87,7 +126,7 @@ resources:
Ref: RouteTablePublic
SubnetId:
Ref: ServerlessSubnetC
-# security group
+ # security group
ServerlessSecurityGroup:
DependsOn:
- ServerlessVPC
@@ -104,7 +143,7 @@ resources:
Tags:
- Key: 'Name'
Value: 'ServerlessSecurityGroup'
-# subnets
+ # subnets
ServerlessSubnetGroup:
DependsOn:
- ServerlessSubnetA
@@ -157,7 +196,7 @@ resources:
Tags:
- Key: 'Name'
Value: 'ServerlessSubnetC'
-# vpc
+ # vpc
ServerlessVPC:
Type: AWS::EC2::VPC
Properties:
@@ -175,7 +214,7 @@ resources:
Ref: ServerlessVPC
InternetGatewayId:
Ref: ServerlessInternetGateway
-# rds
+ # rds
WednesdayRDSCluster:
Type: AWS::RDS::DBCluster
DependsOn:
@@ -230,19 +269,19 @@ resources:
Properties:
Description: Managed policy to provide access to the RDS
PolicyDocument:
- Version: "2012-10-17"
+ Version: '2012-10-17'
Statement:
- - Effect: "Allow"
+ - Effect: 'Allow'
Action:
- rds:*
- Resource: "arn:aws:rds:${env:REGION}:${env:ACCOUNT_ID}:table/*_${env:RDS_PREFIX}_${env:STAGE}"
+ Resource: 'arn:aws:rds:${env:REGION}:${env:ACCOUNT_ID}:table/*_${env:RDS_PREFIX}_${env:STAGE}'
LambdaRDSRole:
Type: AWS::IAM::Role
- Description: "An IAM Role to allow Lambdas to access DDB"
+ Description: 'An IAM Role to allow Lambdas to access DDB'
Properties:
RoleName: ${self:service.name}-${self:provider.stage}-lambda-ddb-role
AssumeRolePolicyDocument:
- Version: "2012-10-17"
+ Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
@@ -258,4 +297,4 @@ resources:
WednesdayRDSPort:
Description: Port of the wednesday RDS instance
Value:
- Fn::GetAtt: [WednesdayRDSCluster, Endpoint.Port]
\ No newline at end of file
+ Fn::GetAtt: [WednesdayRDSCluster, Endpoint.Port]
diff --git a/aws/apigateway-aurora-lambda/setup-lambda.sh b/aws/apigateway-aurora-lambda/setup-lambda.sh
new file mode 100755
index 0000000..266149c
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/setup-lambda.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+set -x
+# Create a lambda
+npm init -y
+touch index.js
+touch data.json
+mkdir tests
+cd tests
+touch index.test.js
\ No newline at end of file
diff --git a/aws/apigateway-aurora-lambda/utils/index.js b/aws/apigateway-aurora-lambda/utils/index.js
new file mode 100644
index 0000000..6f1dfad
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/utils/index.js
@@ -0,0 +1,23 @@
+import get from 'lodash/get';
+
+export const failure = (callback, error) => {
+ console.log('failure', error);
+ return callback(get(error, 'message', 'Something went wrong. Please contact support@bankshift.com'));
+};
+
+export const success = (callback, data) => {
+ console.log('success', JSON.stringify(data));
+ return callback(null, data);
+};
+
+export const getSystemId = event => {
+ if (!event.headers['x-ws-system-id']) {
+ return null;
+ }
+ return { systemId: event.headers['x-ws-system-id'] };
+};
+
+export const logHandler = (event, callback) => {
+ console.log(JSON.stringify({ event }));
+ return callback(event);
+};
diff --git a/aws/apigateway-aurora-lambda/utils/testUtils.js b/aws/apigateway-aurora-lambda/utils/testUtils.js
index 639e642..affee00 100644
--- a/aws/apigateway-aurora-lambda/utils/testUtils.js
+++ b/aws/apigateway-aurora-lambda/utils/testUtils.js
@@ -5,10 +5,44 @@ export function configDB() {
const DBConnectionMock = new SequelizeMock();
const uuidsMock = DBConnectionMock.define('uuids', { uuid: CONSTANTS.uuid, updated_at: CONSTANTS.updatedAt });
+ const employeesMock = DBConnectionMock.define('employees', {
+ employee_name: CONSTANTS.employee.employee_name,
+ office_id: null,
+ updated_at: CONSTANTS.updatedAt
+ });
+ const officesMock = DBConnectionMock.define('offices', {
+ office_name: CONSTANTS.office.office_name,
+ office_address: CONSTANTS.office.office_address,
+ employee_id: null,
+ updated_at: CONSTANTS.updatedAt
+ });
+ const employeeOfficeMock = DBConnectionMock.define('employeeOffice', {
+ office_name: CONSTANTS.office.office_address,
+ office_address: CONSTANTS.office.office_address,
+ employee_name: CONSTANTS.employee.employee_name,
+ employee_id: 1,
+ office_id: 1
+ });
+
uuidsMock.findByPk = query => uuidsMock.findById(query);
uuidsMock.count = () => 1;
+ employeesMock.findByPk = query => employeesMock.findById(query);
+ employeesMock.findAll = query => employeesMock.findAll(query);
+ employeesMock.upsert = query => employeesMock.upsert(query);
+
+ officesMock.findByPk = query => officesMock.findById(query);
+ officesMock.findAll = query => officesMock.findAll(query);
+ officesMock.upsert = query => officesMock.upsert(query);
+
+ employeeOfficeMock.findAll = query => employeeOfficeMock.findAll(query);
+ employeeOfficeMock.upsert = query => employeeOfficeMock.upsert(query);
+ employeeOfficeMock.create = query => employeeOfficeMock.create(query);
+
return {
- uuids: uuidsMock
+ uuids: uuidsMock,
+ employees: employeesMock,
+ offices: officesMock,
+ employeeOffice: employeeOfficeMock
};
}
diff --git a/aws/apigateway-aurora-lambda/utils/tests/indes.test.js b/aws/apigateway-aurora-lambda/utils/tests/indes.test.js
new file mode 100644
index 0000000..8dbd393
--- /dev/null
+++ b/aws/apigateway-aurora-lambda/utils/tests/indes.test.js
@@ -0,0 +1,81 @@
+import { failure, success, logHandler, getSystemId } from '@utils';
+describe('utils/index', () => {
+ let mocks;
+ beforeEach(() => {
+ mocks = {
+ callback: jest.fn()
+ };
+ jest.spyOn(mocks, 'callback');
+ jest.spyOn(console, 'log');
+ });
+
+ describe('failure', () => {
+ it('should print the error and invoke the callback', () => {
+ const error = new Error('some error');
+ failure(mocks.callback, error);
+
+ // ensure that the error is being printed
+ expect(console.log.mock.calls[0][0]).toBe('failure');
+ expect(console.log.mock.calls[0][1]).toBe(error);
+
+ // check if callback has been invoked
+ expect(mocks.callback).toBeCalled();
+
+ // check the parameters that have been supplied to the callback
+ expect(mocks.callback.mock.calls[0][0]).toBe(error.message);
+ expect(mocks.callback.mock.calls[0][1]).toBe(undefined);
+ });
+ });
+
+ describe('success', () => {
+ it('should print the data and invoke the callback', () => {
+ const data = { otpSentAt: '123123' };
+ success(mocks.callback, data);
+
+ // ensure that the data is being printed
+ expect(console.log.mock.calls[0][0]).toBe('success');
+ expect(console.log.mock.calls[0][1]).toBe(JSON.stringify(data));
+
+ // check if callback has been invoked
+ expect(mocks.callback).toBeCalled();
+
+ // check the parameters that have been supplied to callback
+ expect(mocks.callback.mock.calls[0][0]).toBe(null);
+ expect(mocks.callback.mock.calls[0][1]).toBe(data);
+ });
+ });
+
+ describe('logHandler', () => {
+ let mocks;
+ beforeEach(() => {
+ mocks = {
+ callback: jest.fn()
+ };
+ jest.spyOn(mocks, 'callback');
+ });
+ it('should log the event and return callback', async () => {
+ const mockEvent = {};
+ logHandler(mockEvent, mocks.callback);
+ expect(mocks.callback.mock.calls.length).toBe(1);
+ });
+ });
+
+ describe('getSystemId', () => {
+ it('should return getSystemId', async () => {
+ const event = {
+ headers: { 'x-ws-system-id': 'WS' }
+ };
+ const response = getSystemId(event);
+ expect(response).toEqual({
+ systemId: event.headers['x-ws-system-id']
+ });
+ });
+ it('should return null when no auth header is present', async () => {
+ const event = {
+ headers: {}
+ };
+ const response = getSystemId(event);
+ expect(response).toEqual(null);
+ });
+ });
+});