minimonolith 0.6.6 → 0.8.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,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
 
3
- import { createAPI } from './src/createAPI/index.js';
3
+ import { createAPI } from './src/api/index.js';
4
4
  import { runLambdaServer, loadEnvFile } from './src/server/index.js';
5
5
 
6
6
  export { runLambdaServer, loadEnvFile, createAPI, z };
@@ -0,0 +1,32 @@
1
+ name: NPM Publish
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master # Change this to the branch where you want to trigger the workflow
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v3
14
+
15
+ - name: Set up Node.js
16
+ uses: actions/setup-node@v3
17
+ with:
18
+ node-version: 18 # Change this to the Node.js version you need
19
+
20
+ - name: Install dependencies
21
+ run: yarn install
22
+
23
+ - name: Run tests
24
+ run: yarn test
25
+
26
+ - name: Upload coverage to Codecov
27
+ uses: codecov/codecov-action@v3
28
+
29
+ - name: Publish to NPM
30
+ run: yarn publish
31
+ env:
32
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # Set up the NPM_TOKEN in your GitHub repository secrets
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "minimonolith",
3
3
  "type": "module",
4
- "version": "0.6.6",
4
+ "version": "0.8.0",
5
5
  "main": "index.js",
6
6
  "license": "MIT",
7
7
  "scripts": {
@@ -2,9 +2,12 @@ import lambdaAPICreateAPI from 'lambda-api';
2
2
 
3
3
  import { registerHealthService } from '../health/index.js';
4
4
  import { registerDatabaseService } from '../database/index.js';
5
+ import { loadAndSyncModels } from '../model/index.js';
5
6
  import { registerService } from '../service/index.js';
6
7
  import { apiHandler } from './apiHandler.js';
7
8
 
9
+ const ROUTE_CODE = 'CREATE_API';
10
+
8
11
  const addCORS = API => {
9
12
  API.use((req, res, next) => { res.cors(); next(); });
10
13
  API.use((err, req, res, next) => { res.cors(); next(); });
@@ -18,10 +21,6 @@ const addCORS = API => {
18
21
  });
19
22
  };
20
23
 
21
- const addService = API => {
22
-
23
- }
24
-
25
24
  const createAPI = () => {
26
25
  const API = lambdaAPICreateAPI();
27
26
 
@@ -34,7 +33,11 @@ const createAPI = () => {
34
33
 
35
34
  API.registerDatabaseService = async () => { await registerDatabaseService(API) };
36
35
 
37
- API.handler = () => apiHandler(API);
36
+ API.handler = async () => {
37
+ if (API.ORM) API.MODELS = await loadAndSyncModels(API);
38
+ else console.log({ ROUTE_CODE, INFO: 'NO_DATABASE_REGISTERED' });
39
+ return apiHandler(API);
40
+ }
38
41
 
39
42
  API.SERVICES = {};
40
43
 
@@ -1,29 +1,16 @@
1
1
  import Sequelize from 'sequelize';
2
2
  //import SequelizeDynamo from 'dynamo-sequelize';
3
- import { loadAndSyncModels } from './loadModels.js';
4
3
 
5
4
  const ROUTE_CODE = 'DB_CONNECTION';
6
5
 
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 () => {
6
+ const establishConnection = async ORM => {
19
7
  const MAX_RETRIES = 5, INITIAL_WAIT = 100;
20
8
  let connectionRetries = 0, waitTime = INITIAL_WAIT;
21
- const orm = createORMInstance();
22
9
 
23
10
  while (connectionRetries < MAX_RETRIES) {
24
11
  try {
25
- if (!process.env.TEST_ENV) console.log({ ROUTE_CODE, AUTH_INTENT: connectionRetries });
26
- await orm.authenticate();
12
+ console.log({ ROUTE_CODE, AUTH_INTENT: connectionRetries });
13
+ await ORM.authenticate();
27
14
  break;
28
15
 
29
16
  } catch (DATABASE_CONNECTION_ERROR) {
@@ -35,15 +22,19 @@ const establishConnection = async () => {
35
22
  }
36
23
  }
37
24
 
38
- return orm;
25
+ return connectionRetries;
39
26
  };
40
27
 
41
28
  const registerDatabaseService = async API => {
42
29
  try {
43
- const orm = await establishConnection();
44
- API.MODELS = await loadAndSyncModels(orm, Sequelize);
30
+ const { DB_DIALECT, DB_DB, DB_USER, DB_PASS, DB_HOST } = process.env;
31
+ const SEQUELIZE_OPTIONS = { host: DB_HOST, dialect: DB_DIALECT, logging: false };
32
+
33
+ if (!process.env.PROD_ENV) console.log({ ROUTE_CODE, DB_VARS: { DB_USER, DB_PASS, DB_HOST, DB_DB, }});
34
+ API.ORM = new Sequelize(DB_DB, DB_USER, DB_PASS, SEQUELIZE_OPTIONS);
35
+ establishConnection(API.ORM);
45
36
 
46
37
  } catch (DB_ERROR) { console.error({ ROUTE_CODE, DB_ERROR }); }
47
38
  };
48
39
 
49
- export { createORMInstance, establishConnection, registerDatabaseService };
40
+ export { establishConnection, registerDatabaseService };
@@ -1,4 +1,3 @@
1
1
  import { registerDatabaseService } from './databaseService.js';
2
- import { registerModel } from './loadModels.js';
3
2
 
4
- export { registerDatabaseService, registerModel };
3
+ export { registerDatabaseService };
@@ -5,7 +5,7 @@ const healthHandler = () => {
5
5
  const registerHealthService = API => {
6
6
  API.get('/', async (req, res) => {
7
7
  const SERVICE_RESPONSE = healthHandler();
8
- if (!process.env.TEST_ENV) console.log({ SERVICE_RESPONSE });
8
+ console.log({ SERVICE_RESPONSE });
9
9
  return { SERVICE_RESPONSE };
10
10
  });
11
11
  }
@@ -0,0 +1,3 @@
1
+ import { registerModel, loadAndSyncModels } from './loadModels.js';
2
+
3
+ export { registerModel, loadAndSyncModels };
@@ -0,0 +1,27 @@
1
+ import { DataTypes } from 'sequelize';
2
+
3
+ const ROUTE_CODE = 'LOAD_MODELS';
4
+
5
+ const modelSchemas = {};
6
+
7
+ const registerModel = async (SERVICE_NAME, SERVICE_URL) => {
8
+ const SERVICE_MODEL_MODULE = await import(`${SERVICE_URL}model.js`);
9
+ modelSchemas[SERVICE_NAME] = SERVICE_MODEL_MODULE.modelSchema(SERVICE_NAME);
10
+ }
11
+
12
+ const loadAndSyncModels = async API => {
13
+ console.log({ ROUTE_CODE, LOADING_MODELS: 'LOADING_MODELS' });
14
+ const MODELS = Object.keys(modelSchemas).reduce((loadedModels, serviceName) => {
15
+ loadedModels[serviceName] = modelSchemas[serviceName](API.ORM, DataTypes);
16
+ return loadedModels;
17
+ }, {});
18
+
19
+ Object.entries(MODELS).forEach(([serviceName, model]) => { model.associate(MODELS); });
20
+
21
+ console.log({ ROUTE_CODE, SYNCING_ORM: 'SYNCING_ORM' });
22
+ await API.ORM.sync({ alter: process.env.LOCAL_ENV ? true : false });
23
+
24
+ return MODELS;
25
+ };
26
+
27
+ export { registerModel, loadAndSyncModels };
@@ -1,23 +1,33 @@
1
1
  import { validationHandler } from './validationHandler.js';
2
2
 
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
- }
3
+ const validatedMethodHandler = async (event, API, METHOD, ROUTE_CODE) => {
4
+ await validationHandler(event, METHOD, API.MODELS, ROUTE_CODE);
10
5
 
11
- const EVENT_CONTEXT = { event, MODELS: API.MODELS, SERVICES: API.SERVICES, ROUTE_CODE };
12
- const METHOD_RESPONSE = await METHOD.handler(EVENT_CONTEXT);
6
+ const EVENT_CONTEXT = { event, MODELS: API.MODELS, SERVICES: API.SERVICES, ROUTE_CODE };
7
+ const METHOD_RESPONSE = await METHOD.handler(EVENT_CONTEXT);
13
8
 
14
- if (!process.env.TEST_ENV) console.log({ ROUTE_CODE, METHOD_RESPONSE });
15
- return METHOD_RESPONSE;
9
+ return METHOD_RESPONSE;
10
+ };
16
11
 
17
- } catch (METHOD_ERROR) {
18
- if (!process.env.TEST_ENV) console.error({ ROUTE_CODE, METHOD_ERROR });
19
- return { statusCode: 500, body: { ROUTE_CODE, METHOD_ERROR: METHOD_ERROR.toString() } };
12
+ const exposedMethodHandler = async (event, res, API, METHOD, ROUTE_CODE) => {
13
+ try {
14
+ const METHOD_RESPONSE = await validatedMethodHandler(event, API, METHOD, ROUTE_CODE);
15
+ console.log({ ROUTE_CODE, METHOD_RESPONSE: METHOD_RESPONSE.toString() });
16
+ //console.log({ ROUTE_CODE, METHOD_RESPONSE: JSON.stringify(METHOD_RESPONSE, null, 2) });
17
+ res.status(200).json(METHOD_RESPONSE);
18
+
19
+ }
20
+ catch (METHOD_ERROR) {
21
+ switch (METHOD_ERROR.code) {
22
+ case 'VALIDATION_ERROR':
23
+ res.status(400).json({ ROUTE_CODE, VALIDATION_ERROR: METHOD_ERROR.error });
24
+ break;
25
+ default:
26
+ //console.error({ ROUTE_CODE, METHOD_ERROR: JSON.stringify(METHOD_ERROR, null, 2) });
27
+ console.error({ ROUTE_CODE, METHOD_ERROR: METHOD_ERROR.toString() });
28
+ res.status(500).json({ ROUTE_CODE, METHOD_ERROR: METHOD_ERROR.toString() });
29
+ }
20
30
  }
21
31
  };
22
32
 
23
- export { methodHandler };
33
+ export { validatedMethodHandler, exposedMethodHandler };
@@ -1,4 +1,7 @@
1
+ import fs from 'fs';
2
+ import url from 'url';
1
3
  import path from 'path';
4
+ import { z } from 'zod';
2
5
 
3
6
  import { getMethodRouteCode } from './getMethodRouteCode.js';
4
7
 
@@ -7,12 +10,21 @@ const registerMethods = methodNames => async (SERVICE_NAME, SERVICE_URL) => {
7
10
 
8
11
  try {
9
12
  const methods = await methodNames.reduce(async (prevMethods, METHOD_NAME) => {
10
- ROUTE_CODE = 'CREATE_METHOD_' + getMethodRouteCode(SERVICE_NAME, METHOD_NAME);
11
13
  const methods = await prevMethods;
14
+ ROUTE_CODE = 'REGISTER_METHODS_' + getMethodRouteCode(SERVICE_NAME, METHOD_NAME);
15
+ console.log(' FOUND_METHOD', METHOD_NAME);
12
16
 
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, };
17
+ methods[METHOD_NAME] = { VALIDATOR: (MODELS, ROUTE_CODE) => z.undefined() };
18
+ methods[METHOD_NAME].handler =
19
+ (await import(new URL(`${METHOD_NAME}/handler.js`, SERVICE_URL))).default;
20
+
21
+ const VALIDATOR_URL = new URL(`${METHOD_NAME}/valid.js`, SERVICE_URL);
22
+ if (fs.existsSync(url.fileURLToPath(VALIDATOR_URL))) {
23
+ console.log(' FOUND_VALIDATOR');
24
+ const VALIDATOR_BODY = (await import(VALIDATOR_URL)).default;
25
+ methods[METHOD_NAME].VALIDATOR = (MODELS, ROUTE_CODE) =>
26
+ z.object(VALIDATOR_BODY(MODELS, ROUTE_CODE)).strict();
27
+ }
16
28
 
17
29
  return methods;
18
30
  }, Promise.resolve({}));
@@ -1,39 +1,46 @@
1
+ import fs from 'fs';
1
2
  import url from 'url'
2
3
 
3
4
  import { getProjectRoot } from '../path/index.js';
4
- import { registerModel } from '../database/index.js';
5
- import { methodHandler } from './methodHandler.js';
5
+ import { registerModel } from '../model/index.js';
6
+ import { exposedMethodHandler, validatedMethodHandler } from './methodHandler.js';
6
7
  import { getMethodType } from './getMethodType.js';
7
8
  import { getMethodRouteCode } from './getMethodRouteCode.js';
8
9
  import { registerMethods } from './registerMethods.js';
9
10
 
11
+ const ROUTE_CODE = 'REGISTER_SERVICE';
12
+
10
13
  const registerService = async (API, SERVICE_NAME, SRC_FOLDER, MODULE_FOLDER) => {
11
14
  try {
15
+ console.log(ROUTE_CODE, SERVICE_NAME);
12
16
  const PROJECT_ROOT_PATH = getProjectRoot(import.meta.url, MODULE_FOLDER)+'/';
13
17
  const PROJECT_RELATIVE_SERVICE_PATH = './' + SRC_FOLDER + `${SERVICE_NAME}/`;
14
18
  const SERVICE_URL = new URL(PROJECT_RELATIVE_SERVICE_PATH, PROJECT_ROOT_PATH);
15
19
 
16
20
  const SERVICE_MODULE = await import(`${SERVICE_URL}index.js`);
17
- if (SERVICE_MODULE.model) registerModel(SERVICE_NAME, SERVICE_URL);
21
+ if (fs.existsSync(url.fileURLToPath(`${SERVICE_URL}model.js`))) {
22
+ console.log(' FOUND_MODEL');
23
+ registerModel(SERVICE_NAME, SERVICE_URL);
24
+ }
25
+
18
26
  const SERVICE_METHODS = await registerMethods(SERVICE_MODULE.methods)(SERVICE_NAME, SERVICE_URL);
19
27
 
20
28
  API.SERVICES[SERVICE_NAME] = {};
21
29
  Object.keys(SERVICE_METHODS).forEach(METHOD_NAME => {
30
+ const ROUTE_CODE = getMethodRouteCode(SERVICE_NAME, METHOD_NAME);
22
31
 
23
- API.SERVICES[SERVICE_NAME][METHOD_NAME] = async (event, res) => {
24
- const ROUTE_CODE = getMethodRouteCode(SERVICE_NAME, METHOD_NAME);
25
- const { statusCode, body } =
26
- await methodHandler(event, API, SERVICE_METHODS[METHOD_NAME], ROUTE_CODE);
27
- res.status(statusCode).json(body);
32
+ API.SERVICES[SERVICE_NAME][METHOD_NAME] = async body => {
33
+ return await validatedMethodHandler({ body }, API, SERVICE_METHODS[METHOD_NAME], ROUTE_CODE);
28
34
  }
29
35
 
30
36
  const methodType = getMethodType(METHOD_NAME);
31
- API[methodType](`/${SERVICE_NAME}/${METHOD_NAME}`,
32
- API.SERVICES[SERVICE_NAME][METHOD_NAME]);
37
+ API[methodType](`/${SERVICE_NAME}/${METHOD_NAME}`, async (event, res) => {
38
+ await exposedMethodHandler(event, res, API, SERVICE_METHODS[METHOD_NAME], ROUTE_CODE);
39
+ });
33
40
  });
34
41
  } catch (SERVICE_HANDLER_ERROR) {
35
42
  console.error({
36
- ROUTE_CODE: 'SERVICE_HANDLER_ERROR',
43
+ ROUTE_CODE,
37
44
  SERVICE_NAME,
38
45
  SERVICE_HANDLER_ERROR,
39
46
  });
@@ -3,7 +3,12 @@ const validationHandler = async (event, METHOD, MODELS, ROUTE_CODE) => {
3
3
  const validation = await METHOD.VALIDATOR(MODELS, ROUTE_CODE).safeParseAsync(event.body);
4
4
  if (validation.success) return;
5
5
 
6
- return { statusCode: 400, body: { ROUTE_CODE, VALIDATION_ERROR: validation.error.format() } };
6
+ const VALIDATION_ERROR = new Error('VALIDATION_ERROR');
7
+ VALIDATION_ERROR.error = validation.error.format()
8
+ VALIDATION_ERROR.code = 'VALIDATION_ERROR';
9
+ console.error({ ROUTE_CODE, VALIDATION_ERROR: VALIDATION_ERROR.error });
10
+ //console.error({ ROUTE_CODE, VALIDATION_ERROR: JSON.stringify(VALIDATION_ERROR, null, 2) });
11
+ throw VALIDATION_ERROR;
7
12
  };
8
13
 
9
14
  export { validationHandler };
@@ -1,30 +0,0 @@
1
- const ROUTE_CODE = 'LOAD_MODELS';
2
-
3
- const modelSchemas = {};
4
-
5
- export const registerModel = async (SERVICE_NAME, SERVICE_URL) => {
6
- const SERVICE_MODEL_MODULE = await import(`${SERVICE_URL}model.js`);
7
- modelSchemas[SERVICE_NAME] = SERVICE_MODEL_MODULE.modelSchema(SERVICE_NAME);
8
- }
9
-
10
- const loadModels = (orm, types) => {
11
- const MODELS = Object.keys(modelSchemas).reduce((loadedModels, serviceName) => {
12
- loadedModels[serviceName] = modelSchemas[serviceName](orm, types);
13
- return loadedModels;
14
- }, {});
15
-
16
- Object.entries(MODELS).forEach(([serviceName, model]) => { model.associate(MODELS); });
17
- return MODELS;
18
- }
19
-
20
- const loadAndSyncModels = async (orm, types) => {
21
- if (!process.env.TEST_ENV) console.log({ ROUTE_CODE, LOADING_MODELS: 'LOADING_MODELS' });
22
- const MODELS = loadModels(orm, types);
23
-
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
-
27
- return MODELS;
28
- };
29
-
30
- export { loadAndSyncModels };
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes