minimonolith 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,5 +1,7 @@
1
- import createAPI from 'lambda-api';
2
1
  import { z } from 'zod';
3
- import { serviceHandler } from './src/serviceHandler/serviceHandler.js';
4
2
 
5
- export { createAPI, serviceHandler, z };
3
+ import { serviceHandler } from './src/serviceHandler/index.js';
4
+ import { createAPI, apiHandler } from './src/apiHandler/index.js';
5
+ import { runLambdaServer } from './src/lambdaServer/index.js';
6
+
7
+ export { runLambdaServer, createAPI, apiHandler serviceHandler, z };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "minimonolith",
3
3
  "type": "module",
4
- "version": "0.1.4",
4
+ "version": "0.2.0",
5
5
  "main": "index.js",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -15,9 +15,10 @@
15
15
  "zod": "^3.21.4"
16
16
  },
17
17
  "devDependencies": {
18
- "jest": "^29.5.0",
19
18
  "@aws-sdk/client-s3": "^3.304.0",
20
19
  "@aws-sdk/s3-request-presigner": "^3.304.0",
20
+ "dotenv": "^16.0.3",
21
+ "jest": "^29.5.0",
21
22
  "sqlite3": "^5.1.6"
22
23
  }
23
24
  }
@@ -0,0 +1,6 @@
1
+ const apiHandler = API => async (e, context) => {
2
+ console.log({ EVENT_PATH: e.path, EVENT_METHOD: e.httpMethod, });
3
+ return await API.run(e, context);
4
+ };
5
+
6
+ export { apiHandler };
@@ -0,0 +1,24 @@
1
+ import lambdaAPICreateAPI from 'lambda-api';
2
+
3
+ const addCORS = API => {
4
+ API.use((req, res, next) => { res.cors(); next(); });
5
+ API.use((err, req, res, next) => { res.cors(); next(); });
6
+ API.options('/*', (req, res) => {
7
+ res.header('Access-Control-Allow-Origin', '*');
8
+ res.header('Access-Control-Allow-Methods', 'OPTIONS, POST, GET, PUT, PATCH, DELETE');
9
+ res.header('Access-Control-Allow-Headers', '*');
10
+ res.header('Access-Control-Allow-Credentials', true);
11
+ console.log('OPTIONS_SUCCESS');
12
+ res.status(200).send({})
13
+ });
14
+ }
15
+
16
+ const createAPI = () => {
17
+ const API = lambdaAPICreateAPI();
18
+
19
+ addCORS(API);
20
+
21
+ return API;
22
+ }
23
+
24
+ export { createAPI };
@@ -0,0 +1,4 @@
1
+ import { createAPI } from './createAPI.js';
2
+ import { apiHandler } from './apiHandler.js';
3
+
4
+ export { createAPI, apiHandler };
@@ -0,0 +1,14 @@
1
+ const healthCheck = () => {
2
+ return "API running...";
3
+ }
4
+
5
+ const healthHandler = API => {
6
+ API.get('/', async (req, res) => {
7
+ console.log('HEALTH_CHECK ENTERING');
8
+ const response = healthCheck();
9
+ console.log('HEALTH_CHECK SUCCESS');
10
+ return { response };
11
+ });
12
+ }
13
+
14
+ export { healthHandler };
@@ -0,0 +1,3 @@
1
+ import { healthHandler } from './healthHandler.js';
2
+
3
+ export { healthHandler };
package/src/index.js CHANGED
@@ -1,25 +1,13 @@
1
1
  'use strict';
2
2
 
3
- import createAPI from 'lambda-api';
4
- import healthCheck from './healthCheck/index.js';
3
+ import { createAPI, apiHandler } from './apiHandler/index.js';
4
+ import { healthHandler } from './healthHandler/index.js';
5
5
  import { serviceHandler } from './serviceHandler/index.js';
6
6
 
7
7
  const API = createAPI();
8
8
 
9
- // Health Check
10
- API.get('/', async (req, res) => {
11
- console.log('HEALTH_CHECK ENTERING');
12
- const response = healthCheck();
13
- console.log('HEALTH_CHECK SUCCESS');
14
- return { response };
15
- });
9
+ healthHandler(API);
10
+ serviceHandler(API, 'todo');
11
+ const lambdaAPIHandler = apiHandler(API);
16
12
 
17
- serviceHandler(API, 'todo', 'src');
18
-
19
- export const handler = async (e, context) => {
20
- console.log({
21
- EVENT_PATH: e.path,
22
- EVENT_METHOD: e.httpMethod,
23
- });
24
- return await API.run(e, context);
25
- };
13
+ export { lambdaAPIHandler };
@@ -0,0 +1,3 @@
1
+ import { runLambdaServer } from './lambdaServer';
2
+
3
+ export { runLambdaServer };
@@ -0,0 +1,12 @@
1
+ import http from 'http';
2
+
3
+ import './loadEnv.js';
4
+ import { serverHandlerContext } from './serverHandler.js';
5
+
6
+ const runLambdaServer = (lambdaAPIHandler, port) => {
7
+ const serverHandler = serverHandlerContext(lambdaAPIHandler);
8
+ const server = http.createServer(serverHandler);
9
+ server.listen(port);
10
+ }
11
+
12
+ export { runLambdaServer };
@@ -0,0 +1,66 @@
1
+ 'use strict';
2
+
3
+ import url from 'url';
4
+
5
+ const reqBodyPromise = req => {
6
+ return new Promise((resolve, reject) => {
7
+ let data = ''; req.on('data', chunk => { data+=chunk; });
8
+ req.on('end', () => { data!==''? resolve(data):resolve(null); });
9
+ });
10
+ }
11
+
12
+ const parseJWT = token => {
13
+ const tokenString = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
14
+ return tokenString;
15
+ }
16
+
17
+ const serverHandlerContext = lambdaAPIHandler => async (req, res) => {
18
+ try {
19
+ res.setHeader('Access-Control-Allow-Origin', '*');
20
+ res.setHeader('Access-Control-Request-Method', '*');
21
+ res.setHeader('Access-Control-Allow-Methods', 'OPTIONS,GET,POST,PUT,PATCH,DELETE');
22
+ res.setHeader('Access-Control-Allow-Headers', '*');
23
+ if (req.method === 'OPTIONS') {
24
+ res.writeHead(200);
25
+ res.end();
26
+ return;
27
+ }
28
+
29
+ const token =
30
+ req.headers.authorization && req.headers.authorization.startsWith('Bearer ') ?
31
+ parseJWT(req.headers.authorization.split(" ")[1]) : null;
32
+
33
+ const tempQueryString = url.parse(req.url, true).query;
34
+ const queryString = tempQueryString? tempQueryString : undefined;
35
+
36
+ const tempBody = await reqBodyPromise(req);
37
+ const body = tempBody? tempBody : undefined;
38
+
39
+ const e = {
40
+ path: req.url,
41
+ httpMethod: req.method,
42
+ queryStringParameters: queryString,
43
+ requestContext: {
44
+ http: {
45
+ path: req.url,
46
+ method: req.method,
47
+ },
48
+ authorizer: {
49
+ jwt: { claims: token },
50
+ },
51
+ },
52
+ headers: {
53
+ accept: req.headers.accept,
54
+ 'User-Agent': req.headers['user-agent']
55
+ },
56
+ body,
57
+ };
58
+
59
+ const resL = await lambdaAPIHandler(e, null)
60
+
61
+ res.statusCode = resL.statusCode;
62
+ res.end(resL.body);
63
+ } catch(err) { console.log(err); }
64
+ }
65
+
66
+ export { serverHandlerContext };
@@ -1,5 +1,4 @@
1
1
  import { serviceHandler } from './serviceHandler.js';
2
- import { registerMethods } from './registerMethods.js';
3
2
 
4
- export { serviceHandler, registerMethods };
3
+ export { serviceHandler };
5
4
 
@@ -1,3 +0,0 @@
1
- export default () => {
2
- return "API running...";
3
- }
@@ -1,10 +0,0 @@
1
- const handler = async (e, MODELS) => {
2
- const { id } = e.body;
3
-
4
- const todo = await MODELS.todo.findOne({ where: { id: e.body.id } });
5
- await todo.destroy();
6
-
7
- return { statusCode: 200 };
8
- }
9
-
10
- export { handler };
@@ -1,8 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- const VALIDATOR = (MODELS, ROUTE_CODE) =>
4
- z.object({
5
- id: z.number(),
6
- }).strict();
7
-
8
- export { VALIDATOR };
@@ -1,7 +0,0 @@
1
- const handler = async (e, MODELS) => {
2
- const todos = await MODELS.todo.findAll();
3
-
4
- return { statusCode: 200, body: todos };
5
- }
6
-
7
- export { handler };
@@ -1,6 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- const VALIDATOR = (MODELS, ROUTE_CODE) =>
4
- z.undefined();
5
-
6
- export { VALIDATOR };
package/src/todo/index.js DELETED
@@ -1,5 +0,0 @@
1
- import { modelSchema } from './model.js';
2
-
3
- const methods = ['get', 'post', 'delete'];
4
-
5
- export { modelSchema, methods };
package/src/todo/model.js DELETED
@@ -1,8 +0,0 @@
1
- export const modelSchema = serviceName => (sequelize, type) => {
2
- return sequelize.define(serviceName, {
3
- name: {
4
- type: type.STRING,
5
- allowNull: false
6
- },
7
- });
8
- };
@@ -1,9 +0,0 @@
1
- const handler = async (e, MODELS) => {
2
- const { name } = e.body;
3
-
4
- const todo = await MODELS.todo.create({ name });
5
-
6
- return { statusCode: 201, body: todo.toJSON() };
7
- }
8
-
9
- export { handler };
@@ -1,8 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- const VALIDATOR = (MODELS, ROUTE_CODE) =>
4
- z.object({
5
- name: z.string(),
6
- }).strict();
7
-
8
- export { VALIDATOR };
@@ -1,34 +0,0 @@
1
- import jest from 'jest-mock';
2
- import path from 'path';
3
- import { registerMethods } from '../../src/serviceHandler/registerMethods';
4
-
5
- describe('registerMethods', () => {
6
- beforeAll(() => { process.env.TEST_ENVIRONMENT = true; });
7
- afterAll(() => { delete process.env.TEST_ENVIRONMENT; });
8
-
9
- test('should create methods object', async () => {
10
- const methods = registerMethods([
11
- 'serviceHandler/testService/get',
12
- 'serviceHandler/testService/post',
13
- 'serviceHandler/testService/delete',
14
- ]);
15
-
16
- const result = await methods(path.dirname(import.meta.url));
17
- expect(result).toHaveProperty('serviceHandler/testService/get');
18
- expect(result).toHaveProperty('serviceHandler/testService/post');
19
- expect(result).toHaveProperty('serviceHandler/testService/delete');
20
- });
21
-
22
- test('should throw error for invalid path', async () => {
23
- const methods = registerMethods(['serviceHandler/testService/invalid']);
24
- const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
25
-
26
- try {
27
- await methods(path.dirname(import.meta.url));
28
- } catch (error) {
29
- expect(error).toMatch(/Error importing handler or validator/);
30
- expect(consoleSpy).toHaveBeenCalled()
31
- consoleSpy.mockRestore();
32
- }
33
- });
34
- });
@@ -1,95 +0,0 @@
1
- import jest from 'jest-mock';
2
- import { serviceHandler } from '../../src/serviceHandler/serviceHandler.js';
3
-
4
- // Mock API object to simulate the behavior of an API (e.g., Express)
5
- const createMockAPI = () => {
6
- const routes = {};
7
-
8
- return {
9
- get: (path, handler) => (routes[path] = { method: 'get', handler }),
10
- post: (path, handler) => (routes[path] = { method: 'post', handler }),
11
- put: (path, handler) => (routes[path] = { method: 'put', handler }),
12
- delete: (path, handler) => (routes[path] = { method: 'delete', handler }),
13
- routes,
14
- };
15
- };
16
-
17
- describe('serviceHandler', () => {
18
- beforeAll(() => { process.env.TEST_ENVIRONMENT = true; });
19
- afterAll(() => { delete process.env.TEST_ENVIRONMENT; });
20
-
21
- test('should add routes and methods to the API object', async () => {
22
- const mockAPI = createMockAPI();
23
- const serviceName = 'testService';
24
- const SRC_FOLDER = 'test/serviceHandler';
25
-
26
- await serviceHandler(mockAPI, serviceName, SRC_FOLDER);
27
-
28
- // Check if routes are added to the API object
29
- expect(mockAPI.routes).toHaveProperty(`/${serviceName}/get`);
30
- expect(mockAPI.routes).toHaveProperty(`/${serviceName}/post`);
31
- expect(mockAPI.routes).toHaveProperty(`/${serviceName}/delete`);
32
-
33
- // Check if methods are added to the API object
34
- expect(mockAPI.routes[`/${serviceName}/get`].method).toBe('get');
35
- expect(mockAPI.routes[`/${serviceName}/post`].method).toBe('post');
36
- expect(mockAPI.routes[`/${serviceName}/delete`].method).toBe('delete');
37
-
38
- // Check if handlers are added to the API object
39
- expect(mockAPI.routes[`/${serviceName}/get`].handler).toBeInstanceOf(Function);
40
- expect(mockAPI.routes[`/${serviceName}/post`].handler).toBeInstanceOf(Function);
41
- expect(mockAPI.routes[`/${serviceName}/delete`].handler).toBeInstanceOf(Function);
42
- });
43
-
44
- test('should call handlers with the correct ROUTE_CODE, req, and res', async () => {
45
- const mockAPI = createMockAPI();
46
- const serviceName = 'testService';
47
- const SRC_FOLDER = 'test/serviceHandler';
48
-
49
- await serviceHandler(mockAPI, serviceName, SRC_FOLDER);
50
-
51
- const mockReq = { foo: 'bar' };
52
- const mockRes = {
53
- status: jest.fn().mockReturnThis(),
54
- json: jest.fn(),
55
- };
56
-
57
- // Call the GET handler
58
- await mockAPI.routes[`/${serviceName}/get`].handler(mockReq, mockRes);
59
-
60
- // Check if res.status and res.json are called with the correct values
61
- expect(mockRes.status).toHaveBeenCalledWith(200);
62
- expect(mockRes.json).toHaveBeenCalledWith('GET method');
63
-
64
- // Call the POST handler
65
- await mockAPI.routes[`/${serviceName}/post`].handler(mockReq, mockRes);
66
-
67
- // Check if res.status and res.json are called with the correct values
68
- expect(mockRes.status).toHaveBeenCalledWith(200);
69
- expect(mockRes.json).toHaveBeenCalledWith('POST method');
70
-
71
- // Call the DELETE handler
72
- await mockAPI.routes[`/${serviceName}/delete`].handler(mockReq, mockRes);
73
-
74
- // Check if res.status and res.json are called with the correct values
75
- expect(mockRes.status).toHaveBeenCalledWith(200);
76
- expect(mockRes.json).toHaveBeenCalledWith('DELETE method');
77
- });
78
-
79
- test('should log an error if the serviceHandler fails', async () => {
80
- const mockAPI = createMockAPI();
81
- const serviceName = 'nonExistentService';
82
- const SRC_FOLDER = 'test/serviceHandler';
83
-
84
- // Spy on console.error to check if it's called
85
- const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
86
-
87
- await serviceHandler(mockAPI, serviceName, SRC_FOLDER);
88
-
89
- // Check if console.error is called
90
- expect(consoleSpy).toHaveBeenCalled();
91
-
92
- // Restore the original console.error implementation
93
- consoleSpy.mockRestore();
94
- });
95
- });
@@ -1,3 +0,0 @@
1
- export const handler = async () => {
2
- return { statusCode: 200, body: 'DELETE method' };
3
- }
@@ -1,3 +0,0 @@
1
- export const VALIDATOR = () => {
2
- return { safeParseAsync: async () => { return { success: true } } };
3
- }
@@ -1,3 +0,0 @@
1
- export const handler = async () => {
2
- return { statusCode: 200, body: 'GET method' };
3
- }
@@ -1,3 +0,0 @@
1
- export const VALIDATOR = () => {
2
- return { safeParseAsync: async () => { return { success: true } } };
3
- }
@@ -1,3 +0,0 @@
1
- const methods = ['get', 'post', 'delete'];
2
-
3
- export { methods };
@@ -1,3 +0,0 @@
1
- export const handler = async () => {
2
- return { statusCode: 200, body: 'POST method' };
3
- }
@@ -1,3 +0,0 @@
1
- export const VALIDATOR = () => {
2
- return { safeParseAsync: async () => { return { success: true } } };
3
- }