diff --git a/services/community-metrics/src/calcuateCommunitiesMetrics.ts b/services/community-metrics/src/calcuateCommunitiesMetrics.ts index 96ac9d2c..6167cbdc 100644 --- a/services/community-metrics/src/calcuateCommunitiesMetrics.ts +++ b/services/community-metrics/src/calcuateCommunitiesMetrics.ts @@ -11,8 +11,8 @@ export async function calcuateCommunitiesMetrics(): Promise { await communitiesMetrics(); utils.Logger.info('Updated community metrics!'); } catch (error) { - utils.slack.sendSlackMessage('🚨 Error to calculate communities metrics', config.slack.lambdaChannel); utils.Logger.error('Error calcuateCommunitiesMetrics: ', error); + throw error; } } async function communitiesMetrics(): Promise { @@ -152,8 +152,8 @@ export async function calculateGlobalDemographics() { await globalDemographicsService.calculate(); utils.Logger.info('Updated global demographics!'); } catch (error) { - utils.slack.sendSlackMessage('🚨 Error to calculate global demographics', config.slack.lambdaChannel); utils.Logger.error('Error calculateGlobalDemographics: ', error); + throw error; } } @@ -163,8 +163,8 @@ export async function calcuateCommunitiesDemographics() { await communitiesDemographics(); utils.Logger.info('Updated community demographics!'); } catch (error) { - utils.slack.sendSlackMessage('🚨 Error to calculate community demographics', config.slack.lambdaChannel); utils.Logger.error('Error calcuateCommunitiesDemographics: ', error); + throw error; } } async function communitiesDemographics() { diff --git a/services/community-metrics/src/calcuateGlobalMetrics.ts b/services/community-metrics/src/calcuateGlobalMetrics.ts index 46ca659c..11f02d7d 100644 --- a/services/community-metrics/src/calcuateGlobalMetrics.ts +++ b/services/community-metrics/src/calcuateGlobalMetrics.ts @@ -14,8 +14,8 @@ export async function calcuateGlobalMetrics(): Promise { await globalMetrics(); utils.Logger.info('Updated global metrics!'); } catch (error) { - utils.slack.sendSlackMessage('🚨 Error to calculate global metrics', config.slack.lambdaChannel); utils.Logger.error('Error calcuateGlobalMetrics: ', error); + throw error; } } async function globalMetrics(): Promise { diff --git a/services/community-metrics/src/calculateBorrowersPerformance.ts b/services/community-metrics/src/calculateBorrowersPerformance.ts index ae2fe6bf..92c56284 100644 --- a/services/community-metrics/src/calculateBorrowersPerformance.ts +++ b/services/community-metrics/src/calculateBorrowersPerformance.ts @@ -67,8 +67,8 @@ async function calculateBorrowersPerformance(): Promise { } utils.Logger.info('Updated borrowers performance!'); } catch (error) { - utils.slack.sendSlackMessage('🚨 Error to calculate borrowers performance', config.slack.lambdaChannel); utils.Logger.error('Error calculateBorrowersPerformance: ', error); + throw error; } } diff --git a/services/community-metrics/src/updateBeneficiaries.ts b/services/community-metrics/src/updateBeneficiaries.ts index 628e547e..445e7ec1 100644 --- a/services/community-metrics/src/updateBeneficiaries.ts +++ b/services/community-metrics/src/updateBeneficiaries.ts @@ -24,8 +24,8 @@ export async function updateBeneficiaries(): Promise { utils.Logger.info('Beneficiaries updated!'); } catch (error) { await t.rollback(); - utils.slack.sendSlackMessage('🚨 Error to update beneficiaries', config.slack.lambdaChannel); utils.Logger.error('Error update beneficiaries: ', error); + throw error; } } diff --git a/services/community-metrics/src/updateCommunities.ts b/services/community-metrics/src/updateCommunities.ts index cb043d1e..13b3ed93 100644 --- a/services/community-metrics/src/updateCommunities.ts +++ b/services/community-metrics/src/updateCommunities.ts @@ -41,7 +41,7 @@ export async function updateCommunities(): Promise { utils.Logger.info('Communities updated!'); } catch (error) { await t.rollback(); - utils.slack.sendSlackMessage('🚨 Error to update communities', config.slack.lambdaChannel); utils.Logger.error('Error update communities: ', error); + throw error; } } \ No newline at end of file diff --git a/services/community-metrics/src/updateExchangeRates.ts b/services/community-metrics/src/updateExchangeRates.ts index 92f54bd0..7532d4a8 100644 --- a/services/community-metrics/src/updateExchangeRates.ts +++ b/services/community-metrics/src/updateExchangeRates.ts @@ -17,8 +17,8 @@ async function updateExchangeRates(): Promise { } utils.Logger.info('Updated exchange rates!'); } catch (error) { - utils.slack.sendSlackMessage('🚨 Error to update exchange rates', config.slack.lambdaChannel); utils.Logger.error('Error updateExchangeRates: ', error); + throw error; } } diff --git a/services/community-metrics/src/user.ts b/services/community-metrics/src/user.ts index 77dc0b19..d0b179fd 100644 --- a/services/community-metrics/src/user.ts +++ b/services/community-metrics/src/user.ts @@ -30,7 +30,7 @@ export async function verifyDeletedAccounts(): Promise { utils.Logger.info('User accounts to delete verified!'); } catch (error) { await t.rollback(); - utils.slack.sendSlackMessage('🚨 Error to verify deleted accounts', config.slack.lambdaChannel); utils.Logger.error('Error verifyDeletedAccounts: ', error); + throw error; } } diff --git a/services/lambda-alert/.gitignore b/services/lambda-alert/.gitignore new file mode 100644 index 00000000..79235611 --- /dev/null +++ b/services/lambda-alert/.gitignore @@ -0,0 +1,125 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.pnp.* + +build/ +contracts/types/ +.secret +coverage.json + +orbitdb/ +path-for-js-ipfs-repo/ + +.webpack \ No newline at end of file diff --git a/services/lambda-alert/.mocharc.json b/services/lambda-alert/.mocharc.json new file mode 100644 index 00000000..97956889 --- /dev/null +++ b/services/lambda-alert/.mocharc.json @@ -0,0 +1,9 @@ +{ + "extension": ["ts"], + "spec": "tests/**/*.test.ts", + "require": ["ts-node/register", "tsconfig-paths/register"], + "delay": false, + "bail": false, + "watch": false, + "timeout": 30000 +} \ No newline at end of file diff --git a/services/lambda-alert/README.md b/services/lambda-alert/README.md new file mode 100644 index 00000000..3b3d312a --- /dev/null +++ b/services/lambda-alert/README.md @@ -0,0 +1,20 @@ +Configure Serverless Framework +``` +npm install -g serverless + +serverless config credentials \ + --provider aws \ + --key AWS_ACCESS_KEY_ID \ + --secret AWS_SECRET_ACCESS_KEY +``` + +Invoke Local +``` +sls invoke local -f function-name +``` + +Deploy lambda +``` +cd services/proposals +sls deploy --stage STAGE --aws-profile PROFILE +``` \ No newline at end of file diff --git a/services/lambda-alert/handler.ts b/services/lambda-alert/handler.ts new file mode 100644 index 00000000..d94baff1 --- /dev/null +++ b/services/lambda-alert/handler.ts @@ -0,0 +1,24 @@ +import { WebClient } from '@slack/web-api'; + +const SLACK_TOKEN = process.env.SLACK_TOKEN; +const SLACK_CHANNEL = process.env.SLACK_CHANNEL; + +const sendSlackMessage = async (lambda: string) => { + const web = new WebClient(SLACK_TOKEN); + await web.chat.postMessage({ channel: SLACK_CHANNEL!, text: '🚨 Error to run lambda: ' + lambda }).catch(console.error); +} + +export const notify = async (event: any, context: any) => { + try { + console.log(JSON.stringify(event)) + if (event.Records[0]?.Sns?.Message) { + const message = JSON.parse(event.Records[0].Sns.Message); + const lambda = message.Trigger.Dimensions[0].value; + + await sendSlackMessage(lambda); + } + } catch (error) { + console.log(error); + throw error; + } +}; diff --git a/services/lambda-alert/nodemon.json b/services/lambda-alert/nodemon.json new file mode 100644 index 00000000..3a448340 --- /dev/null +++ b/services/lambda-alert/nodemon.json @@ -0,0 +1,19 @@ +{ + "restartable": "rs", + "ignore": [ + ".git", + "node_modules/", + "dist/", + "coverage/" + ], + "watch": [ + "../" + ], + "execMap": { + "ts": "node -r ts-node/register" + }, + "env": { + "NODE_ENV": "development" + }, + "ext": "json,ts" +} \ No newline at end of file diff --git a/services/lambda-alert/package.json b/services/lambda-alert/package.json new file mode 100644 index 00000000..8b74f626 --- /dev/null +++ b/services/lambda-alert/package.json @@ -0,0 +1,28 @@ +{ + "name": "@impactmarket/lambda-alert", + "version": "1.0.0", + "scripts": { + "build": "tsc", + "test": "NODE_ENV=test mocha", + "start": "nodemon -w src handler.ts" + }, + "author": "impact-market", + "license": "Apache-2.0", + "engines": { + "node": "18.x" + }, + "dependencies": { + "@slack/web-api": "6.10.0", + "aws-sdk": "2.1503.0" + }, + "devDependencies": { + "@types/aws-lambda": "8.10.102", + "@types/node": "18.16.4", + "serverless": "3.36.0", + "serverless-plugin-tracing": "2.0.0", + "serverless-webpack": "5.11.0", + "ts-loader": "9.5.0", + "webpack": "5.89.0", + "webpack-node-externals": "3.0.0" + } +} diff --git a/services/lambda-alert/serverless.yml b/services/lambda-alert/serverless.yml new file mode 100644 index 00000000..43e8e5f1 --- /dev/null +++ b/services/lambda-alert/serverless.yml @@ -0,0 +1,32 @@ +service: lambda-alert + +frameworkVersion: '3' +useDotenv: true + +custom: + webpack: + includeModules: true + packager: yarn + +provider: + name: aws + runtime: nodejs18.x + region: ${env:AWS_REGION} + iam: + role: + statements: + - Effect: Allow + Action: + - SNS:Subscribe + Resource: + - "*" + +functions: + notify: + handler: handler.notify + events: + - sns: + arn: "arn:aws:sns:eu-west-1:763241127132:lambdaAlert" + +plugins: + - serverless-webpack \ No newline at end of file diff --git a/services/lambda-alert/tests/alert.test.ts b/services/lambda-alert/tests/alert.test.ts new file mode 100644 index 00000000..e69de29b diff --git a/services/lambda-alert/tsconfig.json b/services/lambda-alert/tsconfig.json new file mode 100644 index 00000000..73f8036e --- /dev/null +++ b/services/lambda-alert/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "baseUrl": "./", + "sourceMap": true, + "target": "es6", + "module": "commonjs", + "strict": true, + "moduleResolution": "node", + "noImplicitThis": true, + "alwaysStrict": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "outDir": "./build", + "noImplicitAny": false, + "skipLibCheck": true, + "useUnknownInCatchVariables": false, + }, + "include": ["src/**/*", "handler.ts"], + "exclude": ["node_modules", "**/*.spec.ts"] +} diff --git a/services/lambda-alert/webpack.config.js b/services/lambda-alert/webpack.config.js new file mode 100644 index 00000000..492781c5 --- /dev/null +++ b/services/lambda-alert/webpack.config.js @@ -0,0 +1,44 @@ +const path = require('path'); +const slsw = require('serverless-webpack'); +const nodeExternals = require('webpack-node-externals'); + +module.exports = { + entry: slsw.lib.entries, + target: 'node', + mode: slsw.lib.webpack.isLocal ? 'development' : 'production', + optimization: { + // We no not want to minimize our code. + minimize: false, + }, + performance: { + // Turn off size warnings for entry points + hints: false, + }, + devtool: 'nosources-source-map', + externals: [nodeExternals()], + resolve: { + extensions: ['.js', '.json', '.ts', '.tsx'], + }, + module: { + rules: [ + { + test: /\.ts(x?)$/, + use: [ + { + loader: 'ts-loader', + }, + ], + }, + { + test: /\.mjs$/, + type: 'javascript/auto', + }, + ], + }, + output: { + libraryTarget: 'commonjs2', + path: path.join(__dirname, '.webpack'), + filename: '[name].js', + sourceMapFilename: '[file].map', + }, +}; \ No newline at end of file diff --git a/services/learn-and-earn/handler.ts b/services/learn-and-earn/handler.ts index 90ddb127..5cb15805 100644 --- a/services/learn-and-earn/handler.ts +++ b/services/learn-and-earn/handler.ts @@ -11,7 +11,7 @@ export const notification = async (event, context) => { utils.Logger.info('Notified users with incomplete courses!'); } catch (error) { utils.Logger.error('Error users with incomplete courses: ', error); - utils.slack.sendSlackMessage('🚨 Error to notify users with incomplete courses', config.slack.lambdaChannel); + throw error; } // notify when a new course is available @@ -20,6 +20,6 @@ export const notification = async (event, context) => { utils.Logger.info('Notified available courses and lessons!'); } catch (error) { utils.Logger.error('Error notify available courses and lessons: ', error); - utils.slack.sendSlackMessage('🚨 Error notify available courses and lessons', config.slack.lambdaChannel); + throw error; } }; diff --git a/services/learn-and-earn/tsconfig.json b/services/learn-and-earn/tsconfig.json index 73f8036e..eb14302b 100644 --- a/services/learn-and-earn/tsconfig.json +++ b/services/learn-and-earn/tsconfig.json @@ -17,6 +17,7 @@ "noImplicitAny": false, "skipLibCheck": true, "useUnknownInCatchVariables": false, + "lib": ["ES2021"] }, "include": ["src/**/*", "handler.ts"], "exclude": ["node_modules", "**/*.spec.ts"] diff --git a/services/microcredit/handler.ts b/services/microcredit/handler.ts index 563e103d..bfe597ac 100644 --- a/services/microcredit/handler.ts +++ b/services/microcredit/handler.ts @@ -22,7 +22,7 @@ export const notification = async (event, context) => { utils.Logger.info('Sent welcome/reminder notification!'); } catch (error) { utils.Logger.error('Error welcome/reminder notification: ', error); - utils.slack.sendSlackMessage('🚨 Error to send welcome/reminder notification', config.slack.lambdaChannel); + throw error; } // if the borrower repaid something in the last month and the performance is above 100%, notify the borrower only once @@ -31,7 +31,7 @@ export const notification = async (event, context) => { utils.Logger.info('notify high performance!'); } catch (error) { utils.Logger.error('Error notify high performance: ', error); - utils.slack.sendSlackMessage('🚨 Error to notify high performance', config.slack.lambdaChannel); + throw error; } // if the borrower repaid something, but the performance is now below 100%, notify the borrower @@ -40,7 +40,7 @@ export const notification = async (event, context) => { utils.Logger.info('notify low performance!'); } catch (error) { utils.Logger.error('Error notify low performance: ', error); - utils.slack.sendSlackMessage('🚨 Error to notify low performance', config.slack.lambdaChannel); + throw error; } // if halfway through the loan, the borrower didn't repay yet, start notifying every 2 weeks @@ -49,7 +49,7 @@ export const notification = async (event, context) => { utils.Logger.info('Remind unpaid loan loan!'); } catch (error) { utils.Logger.error('Error remind unpaid loan loan: ', error); - utils.slack.sendSlackMessage('🚨 Error to remind unpaid loan loan', config.slack.lambdaChannel); + throw error; } // every 2 weeks, remind the borrower about the loan and increasing interest @@ -58,7 +58,7 @@ export const notification = async (event, context) => { utils.Logger.info('Remind increasing interest!'); } catch (error) { utils.Logger.error('Error remind increasing interest: ', error); - utils.slack.sendSlackMessage('🚨 Error to remind increasing interest', config.slack.lambdaChannel); + throw error; } // one week before reaching maturity and 24 hours again @@ -67,6 +67,6 @@ export const notification = async (event, context) => { utils.Logger.info('Remind reaching maturity!'); } catch (error) { utils.Logger.error('Error remind reaching maturity: ', error); - utils.slack.sendSlackMessage('🚨 Error to remind reaching maturity', config.slack.lambdaChannel); + throw error; } }; diff --git a/services/microcredit/src/borrowers.ts b/services/microcredit/src/borrowers.ts index e4746354..4f7707fc 100644 --- a/services/microcredit/src/borrowers.ts +++ b/services/microcredit/src/borrowers.ts @@ -64,7 +64,7 @@ export async function updateBorrowers(): Promise { } catch (error) { await t.rollback(); utils.Logger.error('Error update borrowers: ', error); - utils.slack.sendSlackMessage('🚨 Error to update borrowers', config.slack.lambdaChannel); + throw error; } } @@ -125,7 +125,7 @@ export async function updateCurrentDebt(): Promise { utils.Logger.info('Current debt updated!'); } catch (error) { await t.rollback(); - utils.slack.sendSlackMessage('🚨 Error to update current debt', config.slack.lambdaChannel); utils.Logger.error('Error update current debt: ', error); + throw error; } } \ No newline at end of file diff --git a/services/orchestrator/handler.ts b/services/orchestrator/handler.ts index 6c149269..47647f25 100644 --- a/services/orchestrator/handler.ts +++ b/services/orchestrator/handler.ts @@ -1,17 +1,9 @@ -import { WebClient } from '@slack/web-api'; import AWS from 'aws-sdk'; import axios from 'axios'; const lambda = new AWS.Lambda(); const API_KEY = process.env.API_KEY; const HEROKU_URL = process.env.HEROKU_URL; -const SLACK_TOKEN = process.env.SLACK_TOKEN; -const SLACK_CHANNEL = process.env.SLACK_CHANNEL; - -const sendSlackMessage = async () => { - const web = new WebClient(SLACK_TOKEN); - await web.chat.postMessage({ channel: SLACK_CHANNEL!, text: '🚨 Error to update lambda envs' }).catch(console.error); -} const getHerokuConfigVars = async (appId: string) => { const response = await axios.get( @@ -74,7 +66,6 @@ export const herokuTrigger = async (event: any, context: any) => { }; } catch (error) { console.log(error); - await sendSlackMessage(); throw error; } }; diff --git a/services/orchestrator/package.json b/services/orchestrator/package.json index 39da0105..beaafbcb 100644 --- a/services/orchestrator/package.json +++ b/services/orchestrator/package.json @@ -12,7 +12,6 @@ "node": "18.x" }, "dependencies": { - "@slack/web-api": "6.10.0", "aws-sdk": "2.1503.0", "axios": "1.6.0" },