From 5f621e1ae0e8d47ee1514a9eaea567f515bfc1dd Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Mon, 14 Jul 2025 19:28:29 -0400 Subject: [PATCH 1/7] feature/IO-3303-logging - Logging --- server/job/job-costing.js | 10 +- server/job/job-lifecycle.js | 195 ++++++++++++++++++++---------------- 2 files changed, 111 insertions(+), 94 deletions(-) diff --git a/server/job/job-costing.js b/server/job/job-costing.js index 29c9dbfce..d1fe8b3d7 100644 --- a/server/job/job-costing.js +++ b/server/job/job-costing.js @@ -19,7 +19,7 @@ async function JobCosting(req, res) { const client = req.userGraphQLClient; //Uncomment for further testing - // logger.log("job-costing-start", "DEBUG", req.user.email, jobid, null); + logger.log("job-costing-start", "DEBUG", req.user.email, jobid, null); try { const resp = await client.setHeaders({ Authorization: BearerToken }).request(queries.QUERY_JOB_COSTING_DETAILS, { @@ -47,9 +47,9 @@ async function JobCostingMulti(req, res) { const client = req.userGraphQLClient; //Uncomment for further testing - // logger.log("job-costing-multi-start", "DEBUG", req?.user?.email, null, { - // jobids - // }); + logger.log("job-costing-multi-start", "DEBUG", req?.user?.email, null, { + jobids + }); try { const resp = await client @@ -589,7 +589,7 @@ function GenerateCostingData(job) { amount: Math.round((job.storage_payable || 0) * 100) }); } - + //Is it a DMS Setup? const selectedDmsAllocationConfig = (job.bodyshop.md_responsibility_centers.dms_defaults && diff --git a/server/job/job-lifecycle.js b/server/job/job-lifecycle.js index 7076069f6..209a8c4a0 100644 --- a/server/job/job-lifecycle.js +++ b/server/job/job-lifecycle.js @@ -8,6 +8,7 @@ const getLifecycleStatusColor = require("../utils/getLifecycleStatusColor"); const jobLifecycle = async (req, res) => { // Grab the jobids and statuses from the request body const { jobids, statuses } = req.body; + const { logger } = req; if (!jobids) { return res.status(400).json({ @@ -16,102 +17,118 @@ const jobLifecycle = async (req, res) => { } const jobIDs = _.isArray(jobids) ? jobids : [jobids]; - const client = req.userGraphQLClient; - const resp = await client.request(queries.QUERY_TRANSITIONS_BY_JOBID, { jobids: jobIDs }); - const transitions = resp.transitions; + logger.log("job-lifecycle-start", "DEBUG", req?.user?.email, null, { + jobids: jobIDs + }); + + try { + const client = req.userGraphQLClient; + const resp = await client.request(queries.QUERY_TRANSITIONS_BY_JOBID, { jobids: jobIDs }); + + const transitions = resp.transitions; + + if (!transitions) { + return res.status(200).json({ + jobIDs, + transitions: [] + }); + } + + const transitionsByJobId = _.groupBy(resp.transitions, "jobid"); + + const groupedTransitions = {}; + const allDurations = []; + + for (let jobId in transitionsByJobId) { + let lifecycle = transitionsByJobId[jobId].map((transition) => { + transition.start_readable = transition.start ? moment(transition.start).fromNow() : "N/A"; + transition.end_readable = transition.end ? moment(transition.end).fromNow() : "N/A"; + + if (transition.duration) { + transition.duration_seconds = Math.round(transition.duration / 1000); + transition.duration_minutes = Math.round(transition.duration_seconds / 60); + let duration = moment.duration(transition.duration); + transition.duration_readable = durationToHumanReadable(duration); + } else { + transition.duration_seconds = 0; + transition.duration_minutes = 0; + transition.duration_readable = "N/A"; + } + return transition; + }); + + const durations = calculateStatusDuration(lifecycle, statuses); + + groupedTransitions[jobId] = { + lifecycle, + durations + }; + + if (durations?.summations) { + allDurations.push(durations.summations); + } + } + + const finalSummations = []; + const flatGroupedAllDurations = _.groupBy(allDurations.flat(), "status"); + + const finalStatusCounts = Object.keys(flatGroupedAllDurations).reduce((acc, status) => { + acc[status] = flatGroupedAllDurations[status].length; + return acc; + }, {}); + // Calculate total value of all statuses + const finalTotal = Object.values(flatGroupedAllDurations).reduce((total, statusArr) => { + return total + statusArr.reduce((acc, curr) => acc + curr.value, 0); + }, 0); + + Object.keys(flatGroupedAllDurations).forEach((status) => { + const value = flatGroupedAllDurations[status].reduce((acc, curr) => acc + curr.value, 0); + const humanReadable = durationToHumanReadable(moment.duration(value)); + const percentage = finalTotal > 0 ? (value / finalTotal) * 100 : 0; + const color = getLifecycleStatusColor(status); + const roundedPercentage = `${Math.round(percentage)}%`; + const averageValue = _.size(jobIDs) > 0 ? value / jobIDs.length : 0; + const averageHumanReadable = durationToHumanReadable(moment.duration(averageValue)); + finalSummations.push({ + status, + value, + humanReadable, + percentage, + color, + roundedPercentage, + averageValue, + averageHumanReadable + }); + }); - if (!transitions) { return res.status(200).json({ jobIDs, - transitions: [] - }); - } - - const transitionsByJobId = _.groupBy(resp.transitions, "jobid"); - - const groupedTransitions = {}; - const allDurations = []; - - for (let jobId in transitionsByJobId) { - let lifecycle = transitionsByJobId[jobId].map((transition) => { - transition.start_readable = transition.start ? moment(transition.start).fromNow() : "N/A"; - transition.end_readable = transition.end ? moment(transition.end).fromNow() : "N/A"; - - if (transition.duration) { - transition.duration_seconds = Math.round(transition.duration / 1000); - transition.duration_minutes = Math.round(transition.duration_seconds / 60); - let duration = moment.duration(transition.duration); - transition.duration_readable = durationToHumanReadable(duration); - } else { - transition.duration_seconds = 0; - transition.duration_minutes = 0; - transition.duration_readable = "N/A"; + transition: groupedTransitions, + durations: { + jobs: jobIDs.length, + summations: finalSummations, + totalStatuses: finalSummations.length, + total: finalTotal, + statusCounts: finalStatusCounts, + humanReadable: durationToHumanReadable(moment.duration(finalTotal)), + averageValue: _.size(jobIDs) > 0 ? finalTotal / jobIDs.length : 0, + averageHumanReadable: + _.size(jobIDs) > 0 + ? durationToHumanReadable(moment.duration(finalTotal / jobIDs.length)) + : durationToHumanReadable(moment.duration(0)) } - return transition; }); - - const durations = calculateStatusDuration(lifecycle, statuses); - - groupedTransitions[jobId] = { - lifecycle, - durations - }; - - if (durations?.summations) { - allDurations.push(durations.summations); - } + } catch (error) { + logger.log("job-lifecycle-error", "ERROR", req?.user?.email, null, { + jobids: jobIDs, + statuses: statuses ? JSON.stringify(statuses) : "N/A", + error: error.message + }); + return res.status(500).json({ + error: "Internal server error" + }); } - - const finalSummations = []; - const flatGroupedAllDurations = _.groupBy(allDurations.flat(), "status"); - - const finalStatusCounts = Object.keys(flatGroupedAllDurations).reduce((acc, status) => { - acc[status] = flatGroupedAllDurations[status].length; - return acc; - }, {}); - // Calculate total value of all statuses - const finalTotal = Object.values(flatGroupedAllDurations).reduce((total, statusArr) => { - return total + statusArr.reduce((acc, curr) => acc + curr.value, 0); - }, 0); - - Object.keys(flatGroupedAllDurations).forEach((status) => { - const value = flatGroupedAllDurations[status].reduce((acc, curr) => acc + curr.value, 0); - const humanReadable = durationToHumanReadable(moment.duration(value)); - const percentage = finalTotal > 0 ? (value / finalTotal) * 100 : 0; - const color = getLifecycleStatusColor(status); - const roundedPercentage = `${Math.round(percentage)}%`; - const averageValue = _.size(jobIDs) > 0 ? value / jobIDs.length : 0; - const averageHumanReadable = durationToHumanReadable(moment.duration(averageValue)); - finalSummations.push({ - status, - value, - humanReadable, - percentage, - color, - roundedPercentage, - averageValue, - averageHumanReadable - }); - }); - - return res.status(200).json({ - jobIDs, - transition: groupedTransitions, - durations: { - jobs: jobIDs.length, - summations: finalSummations, - totalStatuses: finalSummations.length, - total: finalTotal, - statusCounts: finalStatusCounts, - humanReadable: durationToHumanReadable(moment.duration(finalTotal)), - averageValue: _.size(jobIDs) > 0 ? finalTotal / jobIDs.length : 0, - averageHumanReadable: - _.size(jobIDs) > 0 - ? durationToHumanReadable(moment.duration(finalTotal / jobIDs.length)) - : durationToHumanReadable(moment.duration(0)) - } - }); }; module.exports = jobLifecycle; From 28abd9707e4886eb748797f088de13e3af3bb712 Mon Sep 17 00:00:00 2001 From: Dave Richer Date: Mon, 14 Jul 2025 19:31:31 -0400 Subject: [PATCH 2/7] feature/IO-3303-logging - Logging --- client/src/contexts/SocketIO/socketProvider.jsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/src/contexts/SocketIO/socketProvider.jsx b/client/src/contexts/SocketIO/socketProvider.jsx index efc77ea7c..18deb9785 100644 --- a/client/src/contexts/SocketIO/socketProvider.jsx +++ b/client/src/contexts/SocketIO/socketProvider.jsx @@ -16,7 +16,7 @@ import { import { useMutation } from "@apollo/client"; import { useTranslation } from "react-i18next"; import { useSplitTreatments } from "@splitsoftware/splitio-react"; -import { SocketContext, INITIAL_NOTIFICATIONS } from "./useSocket.js"; +import { INITIAL_NOTIFICATIONS, SocketContext } from "./useSocket.js"; /** * Socket Provider - Scenario Notifications / Web Socket related items @@ -157,7 +157,10 @@ const SocketProvider = ({ children, bodyshop, navigate, currentUser }) => { auth: { token, bodyshopId: bodyshop.id }, reconnectionAttempts: Infinity, reconnectionDelay: 2000, - reconnectionDelayMax: 10000 + reconnectionDelayMax: 60000, + randomizationFactor: 0.5, + transports: ["websocket", "polling"], // Add this to prefer WebSocket with polling fallback + rememberUpgrade: true }); socketRef.current = socketInstance; From 47a56e32b9d2e053316eeaa172874740bfc22738 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 15 Jul 2025 13:20:41 -0700 Subject: [PATCH 3/7] Add worker process limits for EB config. --- .platform/nginx/conf.d/proxy.conf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.platform/nginx/conf.d/proxy.conf b/.platform/nginx/conf.d/proxy.conf index 2dc60b344..234b7bb98 100644 --- a/.platform/nginx/conf.d/proxy.conf +++ b/.platform/nginx/conf.d/proxy.conf @@ -1,2 +1,8 @@ client_max_body_size 50M; client_body_buffer_size 5M; + +worker_processes auto; +worker_rlimit_nofile 2048; +events { + worker_connections 2048; +} From fade927c9eb802e276308e5995a8b8a1e5861d39 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 15 Jul 2025 13:35:17 -0700 Subject: [PATCH 4/7] Additional change --- .platform/nginx/conf.d/proxy.conf | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.platform/nginx/conf.d/proxy.conf b/.platform/nginx/conf.d/proxy.conf index 234b7bb98..68c9f523f 100644 --- a/.platform/nginx/conf.d/proxy.conf +++ b/.platform/nginx/conf.d/proxy.conf @@ -1,8 +1,4 @@ client_max_body_size 50M; client_body_buffer_size 5M; -worker_processes auto; -worker_rlimit_nofile 2048; -events { - worker_connections 2048; -} +worker_connections 2048; \ No newline at end of file From 63c571942033a47307eddb8667a5c2e1c6789b55 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 15 Jul 2025 13:45:37 -0700 Subject: [PATCH 5/7] Override nginx.conf --- .platform/nginx/conf.d/proxy.conf | 4 +-- .platform/nginx/nginx.conf | 43 +++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 .platform/nginx/nginx.conf diff --git a/.platform/nginx/conf.d/proxy.conf b/.platform/nginx/conf.d/proxy.conf index 68c9f523f..1ab7fa483 100644 --- a/.platform/nginx/conf.d/proxy.conf +++ b/.platform/nginx/conf.d/proxy.conf @@ -1,4 +1,2 @@ client_max_body_size 50M; -client_body_buffer_size 5M; - -worker_connections 2048; \ No newline at end of file +client_body_buffer_size 5M; \ No newline at end of file diff --git a/.platform/nginx/nginx.conf b/.platform/nginx/nginx.conf new file mode 100644 index 000000000..c89519ef4 --- /dev/null +++ b/.platform/nginx/nginx.conf @@ -0,0 +1,43 @@ +#Elastic Beanstalk Nginx Configuration File + +user nginx; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; +worker_processes auto; +worker_rlimit_nofile 200000; + +events { + worker_connections 2048; +} + +http { + server_tokens off; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + include conf.d/*.conf; + + map $http_upgrade $connection_upgrade { + default "upgrade"; + } + + server { + listen 80 default_server; + access_log /var/log/nginx/access.log main; + + client_header_timeout 60; + client_body_timeout 60; + keepalive_timeout 60; + gzip off; + gzip_comp_level 4; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml> + + # Include the Elastic Beanstalk generated locations + include conf.d/elasticbeanstalk/*.conf; + } +} \ No newline at end of file From 9f97ca0336f18e722907f7aa4fc773ce2dbb3ff3 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Tue, 15 Jul 2025 14:06:55 -0700 Subject: [PATCH 6/7] Adjust body and buffer sizes. --- .platform/nginx/nginx.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.platform/nginx/nginx.conf b/.platform/nginx/nginx.conf index c89519ef4..8d8b437cd 100644 --- a/.platform/nginx/nginx.conf +++ b/.platform/nginx/nginx.conf @@ -30,6 +30,8 @@ http { listen 80 default_server; access_log /var/log/nginx/access.log main; + client_max_body_size 50M; + client_body_buffer_size 5M; client_header_timeout 60; client_body_timeout 60; keepalive_timeout 60; From 401e3cff730c91914a5a1e22cdb98f7c4172ec7c Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 16 Jul 2025 12:05:40 -0700 Subject: [PATCH 7/7] Remove all testing due to failed test in RO. --- .platform/nginx/nginx.conf | 45 -------------------------------------- 1 file changed, 45 deletions(-) delete mode 100644 .platform/nginx/nginx.conf diff --git a/.platform/nginx/nginx.conf b/.platform/nginx/nginx.conf deleted file mode 100644 index 8d8b437cd..000000000 --- a/.platform/nginx/nginx.conf +++ /dev/null @@ -1,45 +0,0 @@ -#Elastic Beanstalk Nginx Configuration File - -user nginx; -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; -worker_processes auto; -worker_rlimit_nofile 200000; - -events { - worker_connections 2048; -} - -http { - server_tokens off; - - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - include conf.d/*.conf; - - map $http_upgrade $connection_upgrade { - default "upgrade"; - } - - server { - listen 80 default_server; - access_log /var/log/nginx/access.log main; - - client_max_body_size 50M; - client_body_buffer_size 5M; - client_header_timeout 60; - client_body_timeout 60; - keepalive_timeout 60; - gzip off; - gzip_comp_level 4; - gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml> - - # Include the Elastic Beanstalk generated locations - include conf.d/elasticbeanstalk/*.conf; - } -} \ No newline at end of file