diff --git a/elevate-notification/constants/routes.js b/elevate-notification/constants/routes.js index 8fa9655..20d11c4 100644 --- a/elevate-notification/constants/routes.js +++ b/elevate-notification/constants/routes.js @@ -1,6 +1,14 @@ -module.exports = [ - { route: '/v1/get-user-photo', config: [{ type: 'GET' }] }, - { route: '/v1/get-user-profile', config: [{ type: 'POST' }] }, - { route: '/v1/get-sharelink', config: [{ type: 'PUT' }, { type: 'PATCH' }] }, - { route: '/v1/get-user-details', config: [{ type: 'DELETE' }] }, -]; +module.exports = { + routes: [ + { + sourceRoute: '/notification/v1/email/send', + type: 'POST', + inSequence: false, + orchestrated: false, + targetRoute: { + path: '/notification/v1/email/send', + type: 'POST', + }, + }, + ], +} diff --git a/elevate-notification/controllers/notification.js b/elevate-notification/controllers/notification.js new file mode 100644 index 0000000..f23cba3 --- /dev/null +++ b/elevate-notification/controllers/notification.js @@ -0,0 +1,7 @@ +const routeConfigs = require('../constants/routes') +const requesters = require('../utils/requester') + +const notificationController = { +} + +module.exports = notificationController diff --git a/elevate-notification/controllers/orchestrationController.js b/elevate-notification/controllers/orchestrationController.js new file mode 100644 index 0000000..91c125b --- /dev/null +++ b/elevate-notification/controllers/orchestrationController.js @@ -0,0 +1,13 @@ +const routesConfig = require('../constants/routes') +const notificationController = require('../controllers/notification') +const orchestrationHandler = async (req, res, responses) => { + console.log(req.targetPackages, req.inSequence, req.orchestrated, req.sourceRoute, responses) + console.log(req.body) + const selectedRouteConfig = routesConfig.routes.find((obj) => obj.sourceRoute === req.sourceRoute) + return await notificationController[selectedRouteConfig.targetRoute.functionName](req, res, responses) +} + +const orchestrationController = { + orchestrationHandler, +} +module.exports = orchestrationController diff --git a/elevate-notification/index.js b/elevate-notification/index.js index 7cba384..85a045e 100644 --- a/elevate-notification/index.js +++ b/elevate-notification/index.js @@ -1 +1,37 @@ -const routes = require('./constants/routes'); +const express = require('express') +const router = express.Router() +const routes = require('./constants/routes') +const packageRouter = require('./router') + +const getDependencies = () => { + return ['kafka', 'kafka-connect', 'redis'] +} + +const getPackageMeta = () => { + return { + basePackageName: 'notification', + packageName: 'elevate-notification', + } +} + +const createPackage = (options) => { + return { + router: () => { + console.log('router') + }, + endpoints: [], + dependencies: [], + } +} + +router.get('/', (req, res) => { + res.send('Hello, world! From notification package') +}) + +module.exports = { + dependencies: getDependencies(), + routes, + createPackage, + packageMeta: getPackageMeta(), + packageRouter, +} diff --git a/elevate-notification/router/index.js b/elevate-notification/router/index.js new file mode 100644 index 0000000..a8b43a8 --- /dev/null +++ b/elevate-notification/router/index.js @@ -0,0 +1,10 @@ +const { passThroughRequester } = require('../utils/requester') +const { orchestrationHandler } = require('../controllers/orchestrationController') +const packageRouter = async (req, res, responses) => { + const response = req.orchestrated + ? await orchestrationHandler(req, res, responses) + : await passThroughRequester(req, res) + return response +} + +module.exports = packageRouter diff --git a/elevate-notification/utils/pathParamSetter.js b/elevate-notification/utils/pathParamSetter.js new file mode 100644 index 0000000..130b1fe --- /dev/null +++ b/elevate-notification/utils/pathParamSetter.js @@ -0,0 +1,6 @@ +exports.pathParamSetter = (targetPath, params) => { + return targetPath.replace(/:\w+/g, (match) => { + const fieldName = match.substring(1) + return params[fieldName] || match + }) +} diff --git a/elevate-notification/utils/patternMatcher.js b/elevate-notification/utils/patternMatcher.js new file mode 100644 index 0000000..18cd428 --- /dev/null +++ b/elevate-notification/utils/patternMatcher.js @@ -0,0 +1,16 @@ +exports.matchPathsAndExtractParams = (pattern, url) => { + const paramNames = [] + const regexPattern = new RegExp( + pattern.replace(/:(\w+)/g, (_, paramName) => { + paramNames.push(paramName) + return '([a-zA-Z0-9]+)' + }) + ) + const matchResult = url.match(regexPattern) + if (!matchResult) return false + const params = {} + for (let i = 0; i < paramNames.length; i++) { + params[paramNames[i]] = matchResult[i + 1] + } + return params +} diff --git a/elevate-notification/utils/requester.js b/elevate-notification/utils/requester.js new file mode 100644 index 0000000..146e68f --- /dev/null +++ b/elevate-notification/utils/requester.js @@ -0,0 +1,62 @@ +const http = require('http') +const https = require('https') +const { matchPathsAndExtractParams } = require('../utils/patternMatcher') +const routesConfig = require('../constants/routes') +const { pathParamSetter } = require('../utils/pathParamSetter') +const axios = require('axios') + +const handleInterfaceError = (res, err) => { + console.log('Error: ', err) + res.writeHead(500, { 'Content-Type': 'text/plain' }) + res.end('Interface Server Error') +} + +const passThroughRequester = async (req, res) => { + try { + const sourceBaseUrl = req.protocol + '://' + req.headers.host + '/' + const sourceUrl = new URL(req.originalUrl, sourceBaseUrl) + const route = routesConfig.routes.find((route) => route.sourceRoute === req.sourceRoute) + const params = matchPathsAndExtractParams(route.sourceRoute, req.originalUrl) + const targetRoute = pathParamSetter(route.targetRoute.path, params) + const parsedUrl = new URL(targetRoute, req.baseUrl) + const options = { + method: req.method, + headers: req.headers, + hostname: parsedUrl.hostname, + port: parsedUrl.port, + path: parsedUrl.pathname + sourceUrl.search, + } + console.log({ + sourceBaseUrl, + sourceUrl, + route, + params, + targetRoute, + parsedUrl, + options, + }) + const proxyReq = (parsedUrl.protocol === 'https:' ? https : http).request(options, (proxyRes) => { + res.writeHead(proxyRes.statusCode, proxyRes.headers) + proxyRes.pipe(res, { end: true }) + }) + proxyReq.on('error', (err) => { + handleInterfaceError(res, err) + }) + req.pipe(proxyReq, { end: true }) + } catch (err) { + handleInterfaceError(res, err) + } +} + +const post = async (baseUrl, route, requestBody, headers) => { + const url = baseUrl + route + const response = await axios.post(url, requestBody, { headers }) + return response.data +} + +const requesters = { + passThroughRequester, + post, +} + +module.exports = requesters diff --git a/elevate-scheduler/constants/routes.js b/elevate-scheduler/constants/routes.js index 8fa9655..2f0849c 100644 --- a/elevate-scheduler/constants/routes.js +++ b/elevate-scheduler/constants/routes.js @@ -1,6 +1,44 @@ -module.exports = [ - { route: '/v1/get-user-photo', config: [{ type: 'GET' }] }, - { route: '/v1/get-user-profile', config: [{ type: 'POST' }] }, - { route: '/v1/get-sharelink', config: [{ type: 'PUT' }, { type: 'PATCH' }] }, - { route: '/v1/get-user-details', config: [{ type: 'DELETE' }] }, -]; +module.exports = { + routes: [ + { + sourceRoute: '/scheduler/jobs/list', + type: 'GET', + inSequence: false, + orchestrated: false, + targetRoute: { + path: '/scheduler/jobs/list', + type: 'POST', + }, + }, + { + sourceRoute: '/scheduler/jobs/create', + type: 'POST', + inSequence: false, + orchestrated: false, + targetRoute: { + path: '/scheduler/jobs/create', + type: 'POST', + }, + }, + { + sourceRoute: '/scheduler/jobs/remove', + type: 'POST', + inSequence: false, + orchestrated: false, + targetRoute: { + path: '/scheduler/jobs/remove', + type: 'POST', + }, + }, + { + sourceRoute: '/scheduler/jobs/purge', + type: 'POST', + inSequence: false, + orchestrated: false, + targetRoute: { + path: '/scheduler/jobs/purge', + type: 'POST', + }, + }, + ], +} diff --git a/elevate-scheduler/controllers/orchestrationController.js b/elevate-scheduler/controllers/orchestrationController.js new file mode 100644 index 0000000..761149e --- /dev/null +++ b/elevate-scheduler/controllers/orchestrationController.js @@ -0,0 +1,13 @@ +const routesConfig = require('../constants/routes') +const schedulerController = require('../controllers/user') +const orchestrationHandler = async (req, res, responses) => { + console.log(req.targetPackages, req.inSequence, req.orchestrated, req.sourceRoute, responses) + console.log(req.body) + const selectedRouteConfig = routesConfig.routes.find((obj) => obj.sourceRoute === req.sourceRoute) + return await schedulerController[selectedRouteConfig.targetRoute.functionName](req, res, responses) +} + +const orchestrationController = { + orchestrationHandler, +} +module.exports = orchestrationController diff --git a/elevate-scheduler/controllers/scheduler.js b/elevate-scheduler/controllers/scheduler.js new file mode 100644 index 0000000..d174032 --- /dev/null +++ b/elevate-scheduler/controllers/scheduler.js @@ -0,0 +1,7 @@ +const routeConfigs = require('../constants/routes') +const requesters = require('../utils/requester') + +const schedulerController = { +} + +module.exports = schedulerController diff --git a/elevate-scheduler/index.js b/elevate-scheduler/index.js new file mode 100644 index 0000000..a986aab --- /dev/null +++ b/elevate-scheduler/index.js @@ -0,0 +1,37 @@ +const express = require('express') +const router = express.Router() +const routes = require('./constants/routes') +const packageRouter = require('./router') + +const getDependencies = () => { + return ['kafka', 'kafka-connect', 'redis'] +} + +const getPackageMeta = () => { + return { + basePackageName: 'scheduler', + packageName: 'elevate-scheduler', + } +} + +const createPackage = (options) => { + return { + router: () => { + console.log('router') + }, + endpoints: [], + dependencies: [], + } +} + +router.get('/', (req, res) => { + res.send('Hello, world! From scheduler package') +}) + +module.exports = { + dependencies: getDependencies(), + routes, + createPackage, + packageMeta: getPackageMeta(), + packageRouter, +} diff --git a/elevate-scheduler/router/index.js b/elevate-scheduler/router/index.js new file mode 100644 index 0000000..a8b43a8 --- /dev/null +++ b/elevate-scheduler/router/index.js @@ -0,0 +1,10 @@ +const { passThroughRequester } = require('../utils/requester') +const { orchestrationHandler } = require('../controllers/orchestrationController') +const packageRouter = async (req, res, responses) => { + const response = req.orchestrated + ? await orchestrationHandler(req, res, responses) + : await passThroughRequester(req, res) + return response +} + +module.exports = packageRouter diff --git a/elevate-scheduler/utils/pathParamSetter.js b/elevate-scheduler/utils/pathParamSetter.js new file mode 100644 index 0000000..130b1fe --- /dev/null +++ b/elevate-scheduler/utils/pathParamSetter.js @@ -0,0 +1,6 @@ +exports.pathParamSetter = (targetPath, params) => { + return targetPath.replace(/:\w+/g, (match) => { + const fieldName = match.substring(1) + return params[fieldName] || match + }) +} diff --git a/elevate-scheduler/utils/patternMatcher.js b/elevate-scheduler/utils/patternMatcher.js new file mode 100644 index 0000000..18cd428 --- /dev/null +++ b/elevate-scheduler/utils/patternMatcher.js @@ -0,0 +1,16 @@ +exports.matchPathsAndExtractParams = (pattern, url) => { + const paramNames = [] + const regexPattern = new RegExp( + pattern.replace(/:(\w+)/g, (_, paramName) => { + paramNames.push(paramName) + return '([a-zA-Z0-9]+)' + }) + ) + const matchResult = url.match(regexPattern) + if (!matchResult) return false + const params = {} + for (let i = 0; i < paramNames.length; i++) { + params[paramNames[i]] = matchResult[i + 1] + } + return params +} diff --git a/elevate-scheduler/utils/requester.js b/elevate-scheduler/utils/requester.js new file mode 100644 index 0000000..146e68f --- /dev/null +++ b/elevate-scheduler/utils/requester.js @@ -0,0 +1,62 @@ +const http = require('http') +const https = require('https') +const { matchPathsAndExtractParams } = require('../utils/patternMatcher') +const routesConfig = require('../constants/routes') +const { pathParamSetter } = require('../utils/pathParamSetter') +const axios = require('axios') + +const handleInterfaceError = (res, err) => { + console.log('Error: ', err) + res.writeHead(500, { 'Content-Type': 'text/plain' }) + res.end('Interface Server Error') +} + +const passThroughRequester = async (req, res) => { + try { + const sourceBaseUrl = req.protocol + '://' + req.headers.host + '/' + const sourceUrl = new URL(req.originalUrl, sourceBaseUrl) + const route = routesConfig.routes.find((route) => route.sourceRoute === req.sourceRoute) + const params = matchPathsAndExtractParams(route.sourceRoute, req.originalUrl) + const targetRoute = pathParamSetter(route.targetRoute.path, params) + const parsedUrl = new URL(targetRoute, req.baseUrl) + const options = { + method: req.method, + headers: req.headers, + hostname: parsedUrl.hostname, + port: parsedUrl.port, + path: parsedUrl.pathname + sourceUrl.search, + } + console.log({ + sourceBaseUrl, + sourceUrl, + route, + params, + targetRoute, + parsedUrl, + options, + }) + const proxyReq = (parsedUrl.protocol === 'https:' ? https : http).request(options, (proxyRes) => { + res.writeHead(proxyRes.statusCode, proxyRes.headers) + proxyRes.pipe(res, { end: true }) + }) + proxyReq.on('error', (err) => { + handleInterfaceError(res, err) + }) + req.pipe(proxyReq, { end: true }) + } catch (err) { + handleInterfaceError(res, err) + } +} + +const post = async (baseUrl, route, requestBody, headers) => { + const url = baseUrl + route + const response = await axios.post(url, requestBody, { headers }) + return response.data +} + +const requesters = { + passThroughRequester, + post, +} + +module.exports = requesters