minimonolith 0.6.3 → 0.6.4

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,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  import { createAPI } from './src/createAPI/index.js';
4
- import { runLambdaServer, loadEnvVariables } from './src/server/index.js';
4
+ import { runLambdaServer, loadEnvFile } from './src/server/index.js';
5
5
 
6
- export { runLambdaServer, loadEnvVariables, createAPI, z };
6
+ export { runLambdaServer, loadEnvFile, createAPI, z };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "minimonolith",
3
3
  "type": "module",
4
- "version": "0.6.3",
4
+ "version": "0.6.4",
5
5
  "main": "index.js",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -1,6 +1,11 @@
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);
1
+ const apiHandler = API => async (event, context) => {
2
+ console.log({
3
+ ROUTE_CODE: 'LAMBDA_EVENT',
4
+ EVENT_PATH: event.path,
5
+ EVENT_METHOD: event.httpMethod,
6
+ });
7
+
8
+ return await API.run(event, context);
4
9
  };
5
10
 
6
11
  export { apiHandler };
@@ -0,0 +1,49 @@
1
+ import Sequelize from 'sequelize';
2
+ //import SequelizeDynamo from 'dynamo-sequelize';
3
+ import { loadAndSyncModels } from './loadModels.js';
4
+
5
+ const ROUTE_CODE = 'DB_CONNECTION';
6
+
7
+ const createORMInstance = () => {
8
+ if (process.env.TEST_ENV) return new Sequelize('sqlite::memory:', { logging: false });
9
+
10
+ const { DB_DB, DB_USER, DB_PASS, DB_HOST } = process.env;
11
+ const SEQUELIZE_OPTIONS = { host: DB_HOST, dialect: 'mysql', logging: false };
12
+
13
+ if (process.env.DEV_ENV) console.log({ ROUTE_CODE, DB_VARS: { DB_USER, DB_PASS, DB_HOST, DB_DB, }});
14
+ return new Sequelize(DB_DB, DB_USER, DB_PASS, SEQUELIZE_OPTIONS);
15
+ };
16
+
17
+
18
+ const establishConnection = async () => {
19
+ const MAX_RETRIES = 5, INITIAL_WAIT = 100;
20
+ let connectionRetries = 0, waitTime = INITIAL_WAIT;
21
+ const orm = createORMInstance();
22
+
23
+ while (connectionRetries < MAX_RETRIES) {
24
+ try {
25
+ if (!process.env.TEST_ENV) console.log({ ROUTE_CODE, AUTH_INTENT: connectionRetries });
26
+ await orm.authenticate();
27
+ break;
28
+
29
+ } catch (DATABASE_CONNECTION_ERROR) {
30
+ await new Promise(resolve => setTimeout(resolve, waitTime));
31
+
32
+ connectionRetries += 1;
33
+ const jitter = Math.random() * 0.3 + 0.7;
34
+ waitTime = Math.min(2 * waitTime * jitter, INITIAL_WAIT * Math.pow(2, MAX_RETRIES));
35
+ }
36
+ }
37
+
38
+ return orm;
39
+ };
40
+
41
+ const registerDatabaseService = async API => {
42
+ try {
43
+ const orm = await establishConnection();
44
+ API.MODELS = await loadAndSyncModels(orm, Sequelize);
45
+
46
+ } catch (DB_ERROR) { console.error({ ROUTE_CODE, DB_ERROR }); }
47
+ };
48
+
49
+ export { createORMInstance, establishConnection, registerDatabaseService };
@@ -1,4 +1,4 @@
1
- import { registerDatabaseService } from './registerDatabaseService.js';
1
+ import { registerDatabaseService } from './databaseService.js';
2
2
  import { registerModel } from './loadModels.js';
3
3
 
4
4
  export { registerDatabaseService, registerModel };
@@ -18,11 +18,11 @@ const loadModels = (orm, types) => {
18
18
  }
19
19
 
20
20
  const loadAndSyncModels = async (orm, types) => {
21
- if (!process.env.TEST_ENVIRONMENT) console.log(ROUTE_CODE, 'Loading sequelize models');
21
+ if (!process.env.TEST_ENV) console.log({ ROUTE_CODE, LOADING_MODELS: 'LOADING_MODELS' });
22
22
  const MODELS = loadModels(orm, types);
23
23
 
24
- if (!process.env.TEST_ENVIRONMENT) console.log(ROUTE_CODE, 'Syncing sequelize');
25
- await orm.sync({ alter: process.env.LOCAL ? true : false });
24
+ if (!process.env.TEST_ENV) console.log({ ROUTE_CODE, SYNCING_ORM: 'SYNCING_ORM' });
25
+ await orm.sync({ alter: process.env.LOCAL_ENV ? true : false });
26
26
 
27
27
  return MODELS;
28
28
  };
@@ -0,0 +1,13 @@
1
+ const healthHandler = () => {
2
+ return "API_RUNNING";
3
+ }
4
+
5
+ const registerHealthService = API => {
6
+ API.get('/', async (req, res) => {
7
+ const SERVICE_RESPONSE = healthHandler();
8
+ if (!process.env.TEST_ENV) console.log({ SERVICE_RESPONSE });
9
+ return { SERVICE_RESPONSE };
10
+ });
11
+ }
12
+
13
+ export { registerHealthService };
@@ -1,3 +1,3 @@
1
- import { registerHealthService } from './registerHealthService.js';
1
+ import { registerHealthService } from './healthService.js';
2
2
 
3
3
  export { registerHealthService };
@@ -1,4 +1,4 @@
1
- import { loadEnvVariables } from './loadEnvVariables.js';
1
+ import { loadEnvFile } from './loadEnvFile.js';
2
2
  import { runLambdaServer } from './lambdaServer.js';
3
3
 
4
- export { runLambdaServer, loadEnvVariables };
4
+ export { runLambdaServer, loadEnvFile };
@@ -0,0 +1,14 @@
1
+ import path from 'path';
2
+ import url from 'url';
3
+ import dotenv from 'dotenv';
4
+
5
+ import { getProjectRoot } from '../path/index.js';
6
+
7
+ const loadEnvFile = (ENV_FILE='.env', MODULE_FOLDER='node_modules/') => {
8
+ const projectRootFileUrl = getProjectRoot(import.meta.url, MODULE_FOLDER);
9
+ const projectRootPath = url.fileURLToPath(projectRootFileUrl+'/');
10
+ const envFilePath = path.resolve(projectRootPath, ENV_FILE)
11
+ dotenv.config({ path: envFilePath });
12
+ };
13
+
14
+ export { loadEnvFile };
@@ -2,15 +2,18 @@
2
2
 
3
3
  import url from 'url';
4
4
 
5
- const reqBodyPromise = req => {
5
+ const loadBody = req => {
6
6
  return new Promise((resolve, reject) => {
7
- let data = ''; req.on('data', chunk => { data+=chunk; });
8
- req.on('end', () => { data!==''? resolve(data):resolve(null); });
7
+ let body = '';
8
+ req.on('data', chunk => { body += chunk; });
9
+ req.on('end', () => { resolve(body !== '' ? body : null); });
9
10
  });
10
- }
11
+ };
11
12
 
12
- const parseJWT = token => {
13
- return JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString());
13
+ const getJWTClaims = authHeader => {
14
+ const b64JWT = authHeader.split(" ")[1];
15
+ const b64Claims = b64JWT.split('.')[1];
16
+ return JSON.parse(Buffer.from(b64Claims, 'base64').toString());
14
17
  }
15
18
 
16
19
  const putCORS = res => {
@@ -20,14 +23,13 @@ const putCORS = res => {
20
23
  res.setHeader('Access-Control-Allow-Headers', '*');
21
24
  };
22
25
 
23
- const getEvent = (req, queryString, token, body) => {
26
+ const getLambdaEvent = (req, queryString, jwtClaims, body) => {
24
27
  return {
25
28
  path: req.url,
26
29
  httpMethod: req.method,
27
30
  queryStringParameters: queryString,
28
31
  requestContext: {
29
- http: { path: req.url, method: req.method, },
30
- authorizer: { jwt: { claims: token }, },
32
+ authorizer: { jwt: { claims: jwtClaims }, },
31
33
  },
32
34
  headers: {
33
35
  accept: req.headers.accept,
@@ -42,21 +44,22 @@ const getServerHandler = lambdaHandler => async (req, res) => {
42
44
  putCORS(res);
43
45
  if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; }
44
46
 
45
- const token = req.headers.authorization && req.headers.authorization.startsWith('Bearer ') ?
46
- parseJWT(req.headers.authorization.split(" ")[1]) : null;
47
+ const authHeader = req.headers.authorization;
48
+ const jwtClaims = authHeader?.startsWith('Bearer ') ? getJWTClaim(authHeader) : null;
47
49
 
48
50
  const tempQueryString = url.parse(req.url, true).query;
49
- const queryString = tempQueryString? tempQueryString : undefined;
51
+ const queryString = tempQueryString ? tempQueryString : undefined;
52
+
53
+ const tempBody = await loadBody(req);
54
+ const body = tempBody ? tempBody : undefined;
50
55
 
51
- const tempBody = await reqBodyPromise(req);
52
- const body = tempBody? tempBody : undefined;
56
+ const lambdaEvent = getLambdaEvent(req, queryString, jwtClaims, body);
57
+ const lambdaRes = await lambdaHandler(lambdaEvent, null)
53
58
 
54
- const event = getEvent(req, queryString, token, body);
55
- const resL = await lambdaHandler(event, null)
59
+ res.statusCode = lambdaRes.statusCode;
60
+ res.end(lambdaRes.body);
56
61
 
57
- res.statusCode = resL.statusCode;
58
- res.end(resL.body);
59
- } catch(err) { console.log(err); }
62
+ } catch(SERVER_ERROR) { console.log({ SERVER_ERROR }); }
60
63
  }
61
64
 
62
65
  export { getServerHandler };
@@ -6,9 +6,9 @@ const getUpperSnakeCase = str => {
6
6
  }
7
7
 
8
8
  const getMethodRouteCode = (SERVICE_NAME, METHOD_NAME) => {
9
- const serviceName = getUpperSnakeCase(SERVICE_NAME);
10
- const methodName = getUpperSnakeCase(METHOD_NAME);
11
- return serviceName + '_' + methodName;
9
+ const snakeServiceName = getUpperSnakeCase(SERVICE_NAME);
10
+ const snakeMethodName = getUpperSnakeCase(METHOD_NAME);
11
+ return snakeServiceName + '_' + snakeMethodName;
12
12
  }
13
13
 
14
14
  export { getMethodRouteCode };
@@ -2,10 +2,9 @@ const methodsAvailable = ['post', 'get', 'patch', 'put', 'delete'];
2
2
 
3
3
  const getMethodType = METHOD_NAME => {
4
4
  const methodMatched = METHOD_NAME.match(/^[a-z]*/);
5
- if (!methodMatched || !methodsAvailable.includes(methodMatched[0])) {
6
- console.log('methodMatched "'+methodMatched[0]+'"');
5
+ if (!methodMatched || !methodsAvailable.includes(methodMatched[0]))
7
6
  throw new Error('UNKNOWN_METHOD_TYPE: ' + METHOD_NAME);
8
- }
7
+
9
8
  return methodMatched[0];
10
9
  }
11
10
 
@@ -1,38 +1,22 @@
1
1
  import { validationHandler } from './validationHandler.js';
2
2
 
3
- const methodHandlerErrorThrower = async (event, API, METHOD, ROUTE_CODE) => {
4
-
5
- const validationErrorResponse =
6
- await validationHandler(event, METHOD, API.MODELS, ROUTE_CODE);
7
- if (validationErrorResponse) return validationErrorResponse;
8
-
9
- const eventContext = { event, MODELS: API.MODELS, SERVICES: API.SERVICES, ROUTE_CODE };
10
- const methodResponse = await METHOD.handler(eventContext);
11
-
12
- if (!process.env.TEST_ENVIRONMENT) console.log(ROUTE_CODE, methodResponse);
13
- return methodResponse;
14
- };
3
+ const methodHandler = async (event, API, METHOD, ROUTE_CODE) => {
4
+ try {
5
+ const VALIDATION_ERROR = await validationHandler(event, METHOD, API.MODELS, ROUTE_CODE);
6
+ if (VALIDATION_ERROR) {
7
+ if (!process.env.TEST_ENV) console.error({ ROUTE_CODE, VALIDATION_ERROR });
8
+ return VALIDATION_ERROR;
9
+ }
15
10
 
16
- const methodErrorHandler = (METHOD_ERROR, METHOD, ROUTE_CODE) => {
11
+ const EVENT_CONTEXT = { event, MODELS: API.MODELS, SERVICES: API.SERVICES, ROUTE_CODE };
12
+ const METHOD_RESPONSE = await METHOD.handler(EVENT_CONTEXT);
17
13
 
18
- if (!process.env.TEST_ENVIRONMENT) console.error({ ROUTE_CODE, METHOD_ERROR });
19
- const methodErrorResponse = {
20
- statusCode: 500, body: {
21
- ROUTE_CODE,
22
- METHOD_ERROR: METHOD_ERROR.toString(),
23
- }
24
- };
25
- return methodErrorResponse;
26
- }
14
+ if (!process.env.TEST_ENV) console.log({ ROUTE_CODE, METHOD_RESPONSE });
15
+ return METHOD_RESPONSE;
27
16
 
28
- const methodHandler = async (API, METHOD, ROUTE_CODE, event) => {
29
- try {
30
- if (!process.env.TEST_ENVIRONMENT) console.log(ROUTE_CODE, 'ENTERING');
31
- const methodResponse = await methodHandlerErrorThrower(event, API, METHOD, ROUTE_CODE);
32
- return methodResponse;
33
17
  } catch (METHOD_ERROR) {
34
- const methodErrorResponse = methodErrorHandler(METHOD_ERROR, METHOD, ROUTE_CODE);
35
- return methodErrorResponse;
18
+ if (!process.env.TEST_ENV) console.error({ ROUTE_CODE, METHOD_ERROR });
19
+ return { statusCode: 500, body: { ROUTE_CODE, METHOD_ERROR: METHOD_ERROR.toString() } };
36
20
  }
37
21
  };
38
22
 
@@ -1,33 +1,26 @@
1
1
  import path from 'path';
2
2
 
3
- const registerMethod = async (methodName, SERVICE_URL) => {
4
- const handlerModule = await import(new URL(`./${methodName}/handler.js`, SERVICE_URL));
5
- const validModule = await import(new URL(`./${methodName}/valid.js`, SERVICE_URL));
3
+ import { getMethodRouteCode } from './getMethodRouteCode.js';
6
4
 
7
- return {
8
- handler: handlerModule.handler,
9
- VALIDATOR: validModule.VALIDATOR,
10
- };
11
- };
5
+ const registerMethods = methodNames => async (SERVICE_NAME, SERVICE_URL) => {
6
+ let ROUTE_CODE = 'REGISTER_METHODS';
7
+
8
+ try {
9
+ const methods = await methodNames.reduce(async (prevMethods, METHOD_NAME) => {
10
+ ROUTE_CODE = 'CREATE_METHOD_' + getMethodRouteCode(SERVICE_NAME, METHOD_NAME);
11
+ const methods = await prevMethods;
12
12
 
13
- const registerMethods = methodNames => {
14
- return async SERVICE_URL => {
15
- const methods = {};
13
+ const handlerModule = await import(new URL(`./${METHOD_NAME}/handler.js`, SERVICE_URL));
14
+ const validModule = await import(new URL(`./${METHOD_NAME}/valid.js`, SERVICE_URL));
15
+ methods[METHOD_NAME] = { handler: handlerModule.handler, VALIDATOR: validModule.VALIDATOR, };
16
16
 
17
- for (const methodName of methodNames) {
18
- try {
19
- methods[methodName] = await registerMethod(methodName, SERVICE_URL);
20
- } catch (CREATE_METHOD_ERROR) {
21
- console.error({
22
- ROUTE_CODE: 'CREATE_METHOD_ERROR',
23
- METHOD_NAME: methodName,
24
- CREATE_METHOD_ERROR
25
- });
26
- }
27
- }
17
+ return methods;
18
+ }, Promise.resolve({}));
28
19
 
29
20
  return methods;
30
- };
21
+ } catch (METHOD_ERROR) {
22
+ console.error({ ROUTE_CODE, METHOD_ERROR });
23
+ }
31
24
  };
32
25
 
33
26
  export { registerMethods };
@@ -15,15 +15,15 @@ const registerService = async (API, SERVICE_NAME, SRC_FOLDER, MODULE_FOLDER) =>
15
15
 
16
16
  const SERVICE_MODULE = await import(`${SERVICE_URL}index.js`);
17
17
  if (SERVICE_MODULE.model) registerModel(SERVICE_NAME, SERVICE_URL);
18
- const SERVICE_METHODS = await registerMethods(SERVICE_MODULE.methods)(SERVICE_URL);
18
+ const SERVICE_METHODS = await registerMethods(SERVICE_MODULE.methods)(SERVICE_NAME, SERVICE_URL);
19
19
 
20
20
  API.SERVICES[SERVICE_NAME] = {};
21
21
  Object.keys(SERVICE_METHODS).forEach(METHOD_NAME => {
22
22
 
23
- API.SERVICES[SERVICE_NAME][METHOD_NAME] = async (req, res) => {
23
+ API.SERVICES[SERVICE_NAME][METHOD_NAME] = async (event, res) => {
24
24
  const ROUTE_CODE = getMethodRouteCode(SERVICE_NAME, METHOD_NAME);
25
25
  const { statusCode, body } =
26
- await methodHandler(API, SERVICE_METHODS[METHOD_NAME], ROUTE_CODE, req);
26
+ await methodHandler(event, API, SERVICE_METHODS[METHOD_NAME], ROUTE_CODE);
27
27
  res.status(statusCode).json(body);
28
28
  }
29
29
 
@@ -1,17 +1,12 @@
1
- const validationHandler = async (e, METHOD, MODELS, ROUTE_CODE) => {
1
+ const validationHandler = async (event, METHOD, MODELS, ROUTE_CODE) => {
2
2
 
3
- let validationErrorResponse = null;
4
- const validation = await METHOD.VALIDATOR(MODELS, ROUTE_CODE).safeParseAsync(e.body);
5
- if (!validation.success) {
6
- const validationErrorMessage = {
7
- ROUTE_CODE,
8
- VALIDATION_ERROR: validation.error.format()
9
- };
10
- console.log(validationErrorMessage);
11
- validationErrorResponse = { statusCode: 400, body: validationErrorMessage };
12
- }
3
+ const validation = await METHOD.VALIDATOR(MODELS, ROUTE_CODE).safeParseAsync(event.body);
4
+ if (validation.success) return;
13
5
 
14
- return validationErrorResponse;
6
+ const body = { ROUTE_CODE, VALIDATION_ERROR: validation.error.format() };
7
+ if (!process.env.TEST_ENV) console.error(body);
8
+
9
+ return { statusCode: 400, body };
15
10
  };
16
11
 
17
12
  export { validationHandler };
@@ -1,61 +0,0 @@
1
- import Sequelize from 'sequelize';
2
- //import SequelizeDynamo from 'dynamo-sequelize';
3
- import { loadAndSyncModels } from './loadModels.js';
4
-
5
- const ROUTE_CODE = 'DB_CONNECTION';
6
-
7
- const tryAuthenticating = async (orm, connectionRetries) => {
8
- if (!process.env.TEST_ENVIRONMENT) console.log(ROUTE_CODE, 'Authenticating sequelize intent:', connectionRetries);
9
- await orm.authenticate();
10
- };
11
-
12
- const createORMInstance = () => {
13
- if (process.env.TEST_ENVIRONMENT) {
14
- const orm = new Sequelize('sqlite::memory:');
15
- orm.options.logging = false;
16
- return orm;
17
- } else {
18
- const dialect = 'mysql', logging = false, dialectOptions = null;
19
- const db=process.env.DB_DB, user=process.env.DB_USER,
20
- pass=process.env.DB_PASS, host=process.env.DB_ENDPOINT;
21
- console.log(ROUTE_CODE,
22
- 'Trying to connect using { ' + user + ', ' + pass + ', ' + host + ', ' + db + ' }');
23
- return new Sequelize(db, user, pass, { host, dialect, dialectOptions, logging });
24
- }
25
- };
26
-
27
-
28
- const establishConnection = async () => {
29
- const MAX_RETRIES = 5;
30
- const INITIAL_WAIT_TIME_MS = 100;
31
- let connectionRetries = 0;
32
- let waitTime = INITIAL_WAIT_TIME_MS;
33
- const orm = createORMInstance();
34
-
35
- while (connectionRetries < MAX_RETRIES) {
36
- try {
37
- await tryAuthenticating(orm, connectionRetries);
38
- break;
39
- } catch (databaseConnectionError) {
40
- await new Promise(resolve => setTimeout(resolve, waitTime));
41
- waitTime *= 2; connectionRetries += 1;
42
- }
43
- }
44
-
45
- return orm;
46
- };
47
-
48
- const registerDatabaseService = async API => {
49
- try {
50
- const orm = await establishConnection();
51
- API.MODELS = await loadAndSyncModels(orm, Sequelize);
52
-
53
- } catch (DB_ERROR) {
54
- console.error({
55
- ROUTE_CODE,
56
- DB_ERROR
57
- });
58
- }
59
- };
60
-
61
- export { registerDatabaseService };
@@ -1,14 +0,0 @@
1
- const healthHandler = () => {
2
- return "API running...";
3
- }
4
-
5
- const registerHealthService = API => {
6
- API.get('/', async (req, res) => {
7
- console.log('HEALTH_CHECK ENTERING');
8
- const response = healthHandler();
9
- console.log('HEALTH_CHECK SUCCESS');
10
- return { response };
11
- });
12
- }
13
-
14
- export { registerHealthService };
@@ -1,13 +0,0 @@
1
- import dotenv from 'dotenv';
2
- import path from 'path';
3
- import url from 'url';
4
-
5
- import { getProjectRoot } from '../path/index.js';
6
-
7
- const loadEnvVariables = (MODULE_FOLDER='node_modules/') => {
8
- const projectRootPath = url.fileURLToPath(getProjectRoot(import.meta.url, MODULE_FOLDER)+'/');
9
- const envVarsPath = path.resolve(projectRootPath, '.env')
10
- dotenv.config({ path: envVarsPath });
11
- };
12
-
13
- export { loadEnvVariables };