minimonolith 0.18.0 → 0.19.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/.env.example CHANGED
@@ -1,4 +1,4 @@
1
1
  MM_API_DB_DIALECT=sqlite
2
2
  MM_API_DB_STORAGE=:memory:
3
- MM_API_PROD_ENV=false
4
- MM_API_LOCAL_ENV=true
3
+ MM_API_PROD_ENV=FALSE
4
+ MM_API_DEV_ENV=TRUE
package/README.md CHANGED
@@ -106,7 +106,7 @@ This file validates the `get:id` method's input, ensuring that the provided `id`
106
106
  // todo/get/valid.js
107
107
  import { z, DB_VALIDATION } from 'minimonolith';
108
108
 
109
- export default (MODELS, ROUTE_CODE) => ({
109
+ export default ({ MODELS }) => ({
110
110
  id: z.string()
111
111
  .superRefine(DB_VALIDATION.isSafeInt('id'))
112
112
  .transform(id => parseInt(id))
@@ -114,6 +114,29 @@ export default (MODELS, ROUTE_CODE) => ({
114
114
  })
115
115
  ```
116
116
 
117
+ ## App Environments
118
+
119
+ There are 4 possible environments: DEV=TRUE & PROD=FALSE, DEV=TRUE & PROD=TRUE, DEV=FALSE & PROD=TRUE, DEV=FALSE & PROD=TRUE
120
+ 1. DEV=TRUE & PROD=FALSE: This is the standard DEV environment
121
+ 2. DEV=FALSE & PROD=FALSE: This is the standard QA environment
122
+ 3. DEV=FALSE & PROD=TRUE: This is the stnadard PROD environment
123
+ 4. DEV=TRUE & PROD=TRUE: This allows to test the behavior of PROD within the "new concept" of DEV environment
124
+
125
+ To better understand their relevance:
126
+ 1. The "new concept" DEV environments (DEV=TRUE) aim to make the api crash if an "important" error happens
127
+ 1. Its current only difference is it makes it crash on error at service registration phase
128
+ 2. The "new concept" QA environments (PROD=FALSE) aim at logging data about the system which on production environments would be forbiden personal information
129
+ 1. This is relevant because replication of QA activities (even security QA activities) depend heavily on this
130
+
131
+ The current App environment is determined on the values of MM_API_DEV ENV [TRUE/FALSE] and MM_API_PROD_ENV [TRUE/FALSE]:
132
+
133
+ ```makefile
134
+ # .env standard dev environment
135
+ MM_API_DEV_ENV=TRUE
136
+ MM_API_PROD_ENV=FALSE
137
+ [...]
138
+ ```
139
+
117
140
  ## Database Authentication
118
141
 
119
142
  To set up authentication for the database, you need to provide the necessary environment variables in a `.env` file. Depending on the database dialect the required variables will differ.
@@ -121,27 +144,32 @@ To set up authentication for the database, you need to provide the necessary env
121
144
  For MySQL:
122
145
 
123
146
  ```makefile
147
+ MM_API_DEV_ENV=TRUE
148
+ MM_API_PROD_ENV=FALSE
124
149
  MM_API_DB_DIALECT=mysql
125
150
  MM_API_DB_HOST=<your_database_endpoint>
126
151
  MM_API_DB_PORT=<your_database_port>
127
152
  MM_API_DB_DATABASE=<your_database_name>
128
153
  MM_API_DB_USERNAME=<your_database_username>
129
154
  MM_API_DB_PASSWORD=<your_database_password>
130
- MM_API_LOCAL_ENV=true
131
- MM_API_PROD_ENV=false
132
155
  ```
133
156
 
134
157
  For SQLite:
135
158
 
136
159
  ```makefile
160
+ MM_API_DEV_ENV=TRUE
161
+ MM_API_PROD_ENV=FALSE
137
162
  MM_API_DB_DIALECT=sqlite
138
163
  MM_API_DB_STORAGE=:memory: # For in-memory SQLite database
139
164
  # Or
140
165
  MM_API_DB_STORAGE=path/to/your/sqlite/file.db # For file-based SQLite database
141
166
  MM_API_DB_DATABASE=<your_database_name>
142
- MM_API_LOCAL_ENV=true
143
- MM_API_PROD_ENV=false
144
167
  ```
145
168
 
146
- Make sure to replace the placeholders with your actual database credentials. The `MM_API_LOCAL_ENV` variable is used to allow Sequelize to alter table structure automatically when working locally, while the `MM_API_PROD_ENV` variable controls logging of secret credentials for debugging purposes in non-production environments.
147
-
169
+ Make sure to replace the placeholders with your actual database credentials.
170
+ - `MM_API_DEV_ENV=TRUE` allows Sequelize to alter table structure automatically when working locally
171
+ - `MM_API_PROD_ENV=FALSE` allows logging of DB credentials for debugging purposes in non-production environments
172
+ - We consider high quality logging important for app performance and evolution
173
+ - However we recommend automatic DB credentials updates (daily) High quality logging does not mean
174
+ giving away your infraestructure to hackers
175
+ - At the risk of stating the obvious do not store personal information at the QA database
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "minimonolith",
3
3
  "type": "module",
4
- "version": "0.18.0",
4
+ "version": "0.19.0",
5
5
  "main": "index.js",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -1,5 +1,7 @@
1
+ import { logger } from '../logger/index.js';
2
+
1
3
  const apiHandler = API => async (event, context) => {
2
- console.log({
4
+ logger.log({
3
5
  ROUTE_CODE: 'LAMBDA_EVENT',
4
6
  EVENT_PATH: event.path,
5
7
  EVENT_METHOD: event.httpMethod,
@@ -4,6 +4,7 @@ import { registerHealthService } from '../health/index.js';
4
4
  import { registerDatabaseService } from '../database/index.js';
5
5
  import { loadAndSyncModels } from '../model/index.js';
6
6
  import { registerService } from '../service/index.js';
7
+ import { logger } from '../logger/index.js';
7
8
  import { apiHandler } from './apiHandler.js';
8
9
 
9
10
  const ROUTE_CODE = 'CREATE_API';
@@ -16,7 +17,7 @@ const addCORS = API => {
16
17
  res.header('Access-Control-Allow-Methods', 'OPTIONS, POST, GET, PUT, PATCH, DELETE');
17
18
  res.header('Access-Control-Allow-Headers', '*');
18
19
  res.header('Access-Control-Allow-Credentials', true);
19
- console.log('OPTIONS_SUCCESS');
20
+ //logger.log('OPTIONS_SUCCESS');
20
21
  res.status(200).send({})
21
22
  });
22
23
  };
@@ -35,9 +36,9 @@ const createAPI = () => {
35
36
 
36
37
  API.handler = async () => {
37
38
  if (API.ORM) API.MODELS = await loadAndSyncModels(API);
38
- else console.log({ ROUTE_CODE, INFO: 'NO_DATABASE_REGISTERED' });
39
+ else logger.log({ ROUTE_CODE, INFO: 'NO_DATABASE_REGISTERED' });
39
40
 
40
- console.log({ ROUTE_CODE, INFO: 'LISTENING' });
41
+ logger.log({ ROUTE_CODE, INFO: 'LISTENING' });
41
42
  return apiHandler(API);
42
43
  }
43
44
 
@@ -0,0 +1 @@
1
+ export default ERROR => { if (process.env.MM_API_DEV_ENV==='TRUE') throw ERROR; };
@@ -0,0 +1,3 @@
1
+ import crasher from './crasher.js';
2
+
3
+ export { crasher };
@@ -1,6 +1,9 @@
1
1
  import Sequelize from 'sequelize';
2
2
  //import SequelizeDynamo from 'dynamo-sequelize';
3
3
 
4
+ import { logger } from '../logger/index.js';
5
+ import { crasher } from '../crasher/index.js';
6
+
4
7
  const ROUTE_CODE = 'DB_CONNECTION';
5
8
 
6
9
  const establishConnection = async ORM => {
@@ -9,7 +12,7 @@ const establishConnection = async ORM => {
9
12
 
10
13
  while (connectionRetries < MAX_RETRIES) {
11
14
  try {
12
- console.log({ ROUTE_CODE, AUTH_INTENT: connectionRetries });
15
+ logger.log({ ROUTE_CODE, AUTH_INTENT: connectionRetries });
13
16
  await ORM.authenticate();
14
17
  break;
15
18
 
@@ -32,14 +35,14 @@ const registerDatabaseService = async API => {
32
35
  const SEQUELIZE_OPTIONS = { dialect: MM_API_DB_DIALECT, host: MM_API_DB_HOST,
33
36
  port: MM_API_DB_PORT, storage: MM_API_DB_STORAGE, logging: false };
34
37
 
35
- if (!process.env.PROD_ENV) console.log({ ROUTE_CODE, MM_API_DB_VARS: { MM_API_DB_DIALECT,
38
+ logger.qa({ ROUTE_CODE, MM_API_DB_VARS: { MM_API_DB_DIALECT,
36
39
  MM_API_DB_USER, MM_API_DB_PASS, MM_API_DB_HOST, MM_API_DB_PORT, MM_API_DB_DB, MM_API_DB_STORAGE }});
37
40
  API.ORM = new Sequelize(MM_API_DB_DB, MM_API_DB_USER, MM_API_DB_PASS, SEQUELIZE_OPTIONS);
38
41
  establishConnection(API.ORM);
39
42
 
40
43
  } catch (REGISTER_DB_ERROR) {
41
- console.error({ ROUTE_CODE, REGISTER_DB_ERROR });
42
- if (process.env.MM_API_LOCAL_ENV) throw REGISTER_DB_ERROR;
44
+ logger.error({ ROUTE_CODE, REGISTER_DB_ERROR });
45
+ crasher(REGISTER_DB_ERROR);
43
46
  }
44
47
  };
45
48
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  import url from 'url';
4
4
 
5
+ import { logger } from '../logger/index.js';
6
+
5
7
  const loadBody = req => {
6
8
  return new Promise((resolve, reject) => {
7
9
  let body = '';
@@ -59,7 +61,7 @@ const getServerHandler = lambdaHandler => async (req, res) => {
59
61
  res.statusCode = lambdaRes.statusCode;
60
62
  res.end(lambdaRes.body);
61
63
 
62
- } catch(SERVER_ERROR) { console.log({ SERVER_ERROR }); }
64
+ } catch(SERVER_ERROR) { logger.log({ SERVER_ERROR }); }
63
65
  }
64
66
 
65
67
  export { getServerHandler };
@@ -1,3 +1,5 @@
1
+ import { logger } from '../logger/index.js';
2
+
1
3
  const healthHandler = () => {
2
4
  return "API_RUNNING";
3
5
  }
@@ -5,7 +7,7 @@ const healthHandler = () => {
5
7
  const registerHealthService = API => {
6
8
  API.get('/', async (req, res) => {
7
9
  const SERVICE_RESPONSE = healthHandler();
8
- console.log({ SERVICE_RESPONSE });
10
+ logger.log({ SERVICE_RESPONSE });
9
11
  return { SERVICE_RESPONSE };
10
12
  });
11
13
  }
@@ -0,0 +1,3 @@
1
+ import logger from './logger.js';
2
+
3
+ export { logger };
@@ -0,0 +1,5 @@
1
+ export default {
2
+ error: (...ERROR) => { console.error(...ERROR) },
3
+ log: (...MESSAGE) => { console.log(...MESSAGE) },
4
+ qa: (...MESSAGE) => { if (process.env.MM_API_PROD_ENV!=='TRUE') console.log(...MESSAGE) }
5
+ };
@@ -1,5 +1,7 @@
1
1
  import { DataTypes } from 'sequelize';
2
2
 
3
+ import { logger } from '../logger/index.js';
4
+
3
5
  const ROUTE_CODE = 'LOAD_MODELS';
4
6
 
5
7
  const modelSchemas = {};
@@ -11,19 +13,19 @@ const registerModel = async (SERVICE_NAME, SERVICE_URL) => {
11
13
 
12
14
  const loadAndSyncModels = async API => {
13
15
  const MODELS = Object.keys(modelSchemas).reduce((loadedModels, serviceName) => {
14
- console.log({ ROUTE_CODE, LOADING_MODEL: serviceName });
16
+ logger.log({ ROUTE_CODE, LOADING_MODEL: serviceName });
15
17
  loadedModels[serviceName] = modelSchemas[serviceName](API.ORM, DataTypes);
16
18
  return loadedModels;
17
19
  }, {});
18
20
 
19
21
  Object.entries(MODELS).forEach(([serviceName, model]) => {
20
- console.log({ ROUTE_CODE, ASSOCIATING_MODEL: serviceName });
22
+ logger.log({ ROUTE_CODE, ASSOCIATING_MODEL: serviceName });
21
23
  model.associate(MODELS);
22
24
  });
23
25
 
24
- console.log({ ROUTE_CODE, SYNCING_ORM: 'WAITING_FOR_ORM_SYNCING' });
25
- await API.ORM.sync({ alter: process.env.MM_API_LOCAL_ENV ? true : false });
26
- console.log({ ROUTE_CODE, SYNCING_ORM: 'DONE_ORM_SYNCING' });
26
+ logger.log({ ROUTE_CODE, SYNCING_ORM: 'WAITING_FOR_ORM_SYNCING' });
27
+ await API.ORM.sync({ alter: process.env.MM_API_DEV_ENV==='TRUE' ? true : false });
28
+ logger.log({ ROUTE_CODE, SYNCING_ORM: 'DONE_ORM_SYNCING' });
27
29
 
28
30
  return MODELS;
29
31
  };
@@ -1,3 +1,5 @@
1
+ import { logger } from '../logger/index.js';
2
+
1
3
  const methodHandler = async (res, METHOD, EVENT_CONTEXT) => {
2
4
  const { body, ROUTE_CODE } = EVENT_CONTEXT;
3
5
 
@@ -6,17 +8,16 @@ const methodHandler = async (res, METHOD, EVENT_CONTEXT) => {
6
8
  const validation = await METHOD.VALIDATOR(EVENT_CONTEXT).safeParseAsync(body);
7
9
  if (validation.success) {
8
10
  const METHOD_RESPONSE = await METHOD.handler(EVENT_CONTEXT);
9
- //const METHOD_RESPONSE = await internalMethodHandler(METHOD, EVENT_CONTEXT);
10
- console.log({ ROUTE_CODE, METHOD_RESPONSE: METHOD_RESPONSE ? METHOD_RESPONSE.toString(): METHOD_RESPONSE });
11
+ logger.log({ ROUTE_CODE, METHOD_RESPONSE: METHOD_RESPONSE ? METHOD_RESPONSE.toString(): METHOD_RESPONSE });
11
12
  res.status(200).json(METHOD_RESPONSE); return;
12
13
  }
13
14
 
14
15
  const VALIDATION_ERROR = validation.error.format();
15
- console.error({ ROUTE_CODE, VALIDATION_ERROR });
16
+ logger.error({ ROUTE_CODE, VALIDATION_ERROR });
16
17
  res.status(400).json({ ROUTE_CODE, VALIDATION_ERROR });
17
18
  }
18
19
  catch (METHOD_ERROR) {
19
- console.error({ ROUTE_CODE, METHOD_ERROR: METHOD_ERROR.stack });
20
+ logger.error({ ROUTE_CODE, METHOD_ERROR: METHOD_ERROR.stack });
20
21
  res.status(500).json({ ROUTE_CODE, METHOD_ERROR: METHOD_ERROR.stack.toString() });
21
22
  }
22
23
  };
@@ -1,7 +1,10 @@
1
1
  import fs from 'fs';
2
2
  import url from 'url';
3
3
  import path from 'path';
4
+
4
5
  import { z, optionalZObject } from '../zod/index.js';
6
+ import { logger } from '../logger/index.js';
7
+ import { crasher } from '../crasher/index.js';
5
8
 
6
9
  const registerMethod = async (SERVICE_NAME, SERVICE_URL, METHOD_NAME, METHOD_ROUTE_CODE) => {
7
10
 
@@ -9,14 +12,14 @@ const registerMethod = async (SERVICE_NAME, SERVICE_URL, METHOD_NAME, METHOD_ROU
9
12
  const method = { VALIDATOR: EVENT_CONTEXT => z.undefined() };
10
13
 
11
14
  try {
12
- console.log(' FOUND_METHOD', METHOD_NAME);
15
+ logger.log(' FOUND_METHOD', METHOD_NAME);
13
16
 
14
17
  method.handler = (await import(new URL(`${METHOD_NAME}/index.js`, SERVICE_URL))).default;
15
18
 
16
19
  const VALIDATOR_URL = new URL(`${METHOD_NAME}/valid.js`, SERVICE_URL);
17
20
  if (fs.existsSync(url.fileURLToPath(VALIDATOR_URL))) {
18
21
 
19
- console.log(' FOUND_VALIDATOR');
22
+ logger.log(' FOUND_VALIDATOR');
20
23
 
21
24
  const Z_ELEMENTS = (await import(VALIDATOR_URL)).default;
22
25
  method.VALIDATOR = EVENT_CONTEXT => optionalZObject(Z_ELEMENTS(EVENT_CONTEXT));
@@ -25,8 +28,8 @@ const registerMethod = async (SERVICE_NAME, SERVICE_URL, METHOD_NAME, METHOD_ROU
25
28
  return method;
26
29
 
27
30
  } catch (REGISTER_METHOD_ERROR) {
28
- console.error({ ROUTE_CODE, REGISTER_METHOD_ERROR, STACK_TRASE: REGISTER_METHOD_ERROR.stack });
29
- if (process.env.MM_API_LOCAL_ENV) { throw REGISTER_METHOD_ERROR; }
31
+ logger.error({ ROUTE_CODE, REGISTER_METHOD_ERROR, STACK_TRASE: REGISTER_METHOD_ERROR.stack });
32
+ crasher(REGISTER_METHOD_ERROR);
30
33
  }
31
34
  };
32
35
 
@@ -3,6 +3,8 @@ import url from 'url'
3
3
 
4
4
  import { getProjectRoot } from '../path/index.js';
5
5
  import { registerModel } from '../model/index.js';
6
+ import { logger } from '../logger/index.js';
7
+ import { crasher } from '../crasher/index.js';
6
8
  import { methodHandler } from './methodHandler.js';
7
9
  import { getMethodType } from './getMethodType.js';
8
10
  import { getMethodRouteCode } from './getMethodRouteCode.js';
@@ -13,23 +15,23 @@ const ROUTE_CODE = 'REGISTER_SERVICE';
13
15
 
14
16
  const registerService = async (API, SERVICE_NAME, SRC_FOLDER, MODULE_FOLDER) => {
15
17
  try {
16
- console.log(ROUTE_CODE, SERVICE_NAME);
18
+ logger.log(ROUTE_CODE, SERVICE_NAME);
17
19
  const PROJECT_ROOT_PATH = getProjectRoot(import.meta.url, MODULE_FOLDER)+'/';
18
20
  const PROJECT_RELATIVE_SERVICE_PATH = './' + SRC_FOLDER + `${SERVICE_NAME}/`;
19
21
  const SERVICE_URL = new URL(PROJECT_RELATIVE_SERVICE_PATH, PROJECT_ROOT_PATH);
20
22
 
21
23
  const SERVICE_MODULE = await import(`${SERVICE_URL}index.js`);
22
24
  if (fs.existsSync(url.fileURLToPath(`${SERVICE_URL}model.js`))) {
23
- console.log(' FOUND_MODEL');
25
+ logger.log(' FOUND_MODEL');
24
26
  registerModel(SERVICE_NAME, SERVICE_URL);
25
27
  }
26
28
 
27
- console.log('METHODS_TO_BE_REGISTERED', SERVICE_MODULE.default);
29
+ logger.log('METHODS_TO_BE_REGISTERED', SERVICE_MODULE.default);
28
30
 
29
31
  API.SERVICES[SERVICE_NAME] = {}; const SERVICE_METHODS = [];
30
32
  for (const METHOD_CODE of SERVICE_MODULE.default) {
31
33
  const { METHOD_NAME, METHOD_ROUTE } = parseMethodCode(SERVICE_NAME, METHOD_CODE);
32
- console.log('REGISTERING_ROUTE', METHOD_ROUTE);
34
+ logger.log('REGISTERING_ROUTE', METHOD_ROUTE);
33
35
 
34
36
  const METHOD_ROUTE_CODE = getMethodRouteCode(SERVICE_NAME, METHOD_NAME);
35
37
 
@@ -55,13 +57,13 @@ const registerService = async (API, SERVICE_NAME, SRC_FOLDER, MODULE_FOLDER) =>
55
57
  });
56
58
  };
57
59
  } catch (REGISTER_SERVICE_ERROR) {
58
- console.error({
60
+ logger.error({
59
61
  ROUTE_CODE,
60
62
  SERVICE_NAME,
61
63
  REGISTER_SERVICE_ERROR,
62
64
  STACK_TRASE: REGISTER_SERVICE_ERROR.stack,
63
65
  });
64
- if (process.env.MM_API_LOCAL_ENV) { throw REGISTER_SERVICE_ERROR; }
66
+ crasher(REGISTER_SERVICE_ERROR);
65
67
  }
66
68
  }
67
69
 
@@ -1,16 +0,0 @@
1
- const validationHandler = async (res, METHOD, EVENT_CONTEXT) => {
2
- const { body, ROUTE_CODE } = EVENT_CONTEXT;
3
-
4
- const validation = await METHOD.VALIDATOR(EVENT_CONTEXT).safeParseAsync(body);
5
- if (validation.success) {
6
- const METHOD_RESPONSE = await METHOD.handler(EVENT_CONTEXT);
7
- console.log({ ROUTE_CODE, METHOD_RESPONSE: METHOD_RESPONSE ? METHOD_RESPONSE.toString(): METHOD_RESPONSE });
8
- res.status(200).json(METHOD_RESPONSE); return;
9
- }
10
-
11
- const VALIDATION_ERROR = validation.error.format();
12
- console.error({ ROUTE_CODE, VALIDATION_ERROR });
13
- res.status(400).json({ ROUTE_CODE, VALIDATION_ERROR });
14
- };
15
-
16
- export { validationHandler };