From 7688f221618ccc6ca9e307ed9b5e510f42703282 Mon Sep 17 00:00:00 2001 From: Dave Date: Wed, 1 Apr 2026 14:39:34 -0400 Subject: [PATCH 1/2] release/2026-04-03 - Clean up localstack endpoints / env check --- server/chatter/chatter-client.js | 11 +++--- server/data/carfax.js | 5 ++- server/data/chatter.js | 9 +++-- server/email/mailer.js | 9 ++--- server/utils/instanceMgr.js | 62 +++++++++++++++++++++++++++----- server/utils/logger.js | 7 ++-- server/utils/s3.js | 14 +++----- 7 files changed, 73 insertions(+), 44 deletions(-) diff --git a/server/chatter/chatter-client.js b/server/chatter/chatter-client.js index 047e94459..b9b024737 100644 --- a/server/chatter/chatter-client.js +++ b/server/chatter/chatter-client.js @@ -1,20 +1,17 @@ const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager"); const { defaultProvider } = require("@aws-sdk/credential-provider-node"); -const { isString, isEmpty } = require("lodash"); +const { InstanceRegion, InstanceIsLocalStackEnabled, InstanceLocalStackEndpoint } = require("../utils/instanceMgr"); const CHATTER_BASE_URL = process.env.CHATTER_API_BASE_URL || "https://api.chatterresearch.com"; -const AWS_REGION = process.env.AWS_REGION || "ca-central-1"; // Configure SecretsManager client with localstack support const secretsClientOptions = { - region: AWS_REGION, + region: InstanceRegion(), credentials: defaultProvider() }; -const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME); - -if (isLocal) { - secretsClientOptions.endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:4566`; +if (InstanceIsLocalStackEnabled()) { + secretsClientOptions.endpoint = InstanceLocalStackEndpoint(); } const secretsClient = new SecretsManagerClient(secretsClientOptions); diff --git a/server/data/carfax.js b/server/data/carfax.js index 250d553bb..ec5c7b4a2 100644 --- a/server/data/carfax.js +++ b/server/data/carfax.js @@ -3,7 +3,7 @@ const Dinero = require("dinero.js"); const moment = require("moment-timezone"); const logger = require("../utils/logger"); const InstanceManager = require("../utils/instanceMgr").default; -const { isString, isEmpty } = require("lodash"); +const { InstanceIsLocalStackEnabled } = require("../utils/instanceMgr"); const fs = require("fs"); const client = require("../graphql-client/graphql-client").client; const { sendServerEmail, sendMexicoBillingEmail } = require("../email/sendemail"); @@ -35,10 +35,9 @@ const S3_BUCKET_NAME = InstanceManager({ rome: "rome-carfax-uploads" }); const region = InstanceManager.InstanceRegion; -const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME); const uploadToS3 = (jsonObj, bucketName = S3_BUCKET_NAME) => { - const webPath = isLocal + const webPath = InstanceIsLocalStackEnabled() ? `https://${bucketName}.s3.localhost.localstack.cloud:4566/${jsonObj.filename}` : `https://${bucketName}.s3.${region}.amazonaws.com/${jsonObj.filename}`; diff --git a/server/data/chatter.js b/server/data/chatter.js index 86182fbf9..5da405b35 100644 --- a/server/data/chatter.js +++ b/server/data/chatter.js @@ -5,7 +5,8 @@ const logger = require("../utils/logger"); const fs = require("fs"); const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager"); const { defaultProvider } = require("@aws-sdk/credential-provider-node"); -const { isString, isEmpty } = require("lodash"); +const { InstanceIsLocalStackEnabled, InstanceLocalStackEndpoint } = require("../utils/instanceMgr"); + let Client = require("ssh2-sftp-client"); const client = require("../graphql-client/graphql-client").client; @@ -151,10 +152,8 @@ async function getPrivateKey() { credentials: defaultProvider() }; - const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME); - - if (isLocal) { - secretsClientOptions.endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:4566`; + if (InstanceIsLocalStackEnabled()) { + secretsClientOptions.endpoint = InstanceLocalStackEndpoint(); } const client = new SecretsManagerClient(secretsClientOptions); diff --git a/server/email/mailer.js b/server/email/mailer.js index 6134053b6..41300caa9 100644 --- a/server/email/mailer.js +++ b/server/email/mailer.js @@ -1,20 +1,17 @@ -const { isString, isEmpty } = require("lodash"); const { defaultProvider } = require("@aws-sdk/credential-provider-node"); -const { InstanceRegion } = require("../utils/instanceMgr"); +const { InstanceRegion, InstanceIsLocalStackEnabled, InstanceLocalStackEndpoint } = require("../utils/instanceMgr"); const aws = require("@aws-sdk/client-ses"); const nodemailer = require("nodemailer"); const logger = require("../utils/logger"); -const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME); - const sesConfig = { apiVersion: "latest", credentials: defaultProvider(), region: InstanceRegion() }; -if (isLocal) { - sesConfig.endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:4566`; +if (InstanceIsLocalStackEnabled()) { + sesConfig.endpoint = InstanceLocalStackEndpoint(); logger.logger.debug(`SES Mailer set to LocalStack end point: ${sesConfig.endpoint}`); } diff --git a/server/utils/instanceMgr.js b/server/utils/instanceMgr.js index 07759ac32..29e90eda1 100644 --- a/server/utils/instanceMgr.js +++ b/server/utils/instanceMgr.js @@ -7,14 +7,24 @@ * @property { string | object | function } promanager Return this prop if Rome. * @property { string | object | function } imex Return this prop if Rome. */ +const { isString, isEmpty } = require("lodash"); -function InstanceManager({ args, instance, debug, executeFunction, rome, promanager, imex }) { +/** + * InstanceManager is a utility function that determines which property to return based on the current instance type. + * @param param0 + * @param param0.args + * @param param0.instance + * @param param0.debug + * @param param0.executeFunction + * @param param0.rome + * @param param0.promanager + * @param param0.imex + * @returns {*|null} + * @constructor + */ +const InstanceManager = ({ args, instance, debug, executeFunction, rome, promanager, imex }) => { let propToReturn = null; - //TODO: Remove after debugging. - if (promanager) { - console.trace("ProManager Prop was used"); - } switch (instance || process.env.INSTANCE) { case "IMEX": propToReturn = imex; @@ -50,15 +60,42 @@ function InstanceManager({ args, instance, debug, executeFunction, rome, promana } if (executeFunction && typeof propToReturn === "function") return propToReturn(...args); return propToReturn === undefined ? null : propToReturn; -} +}; -exports.InstanceRegion = () => +/** + * Returns the AWS region to be used for the current instance, which is determined by the INSTANCE environment variable. + * @returns {*} + * @constructor + */ +const InstanceRegion = () => InstanceManager({ imex: "ca-central-1", rome: "us-east-2" }); -exports.InstanceEndpoints = () => +/** + * Checks if the instance is configured to use LocalStack by verifying the presence of the LOCALSTACK_HOSTNAME + * environment variable. + * @returns {boolean} + * @constructor + */ +const InstanceIsLocalStackEnabled = () => + isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME); + +/** + * Returns the LocalStack endpoint URL based on the LOCALSTACK_HOSTNAME environment variable. + * @returns {`http://${*}:4566`} + * @constructor + */ +const InstanceLocalStackEndpoint = () => `http://${process.env.LOCALSTACK_HOSTNAME}:4566`; + +/** + * Returns the appropriate endpoints for the current instance, which can be used for making API calls or other network + * requests. + * @returns {*|null} + * @constructor + */ +const InstanceEndpoints = () => InstanceManager({ imex: process.env?.NODE_ENV === "development" @@ -74,4 +111,11 @@ exports.InstanceEndpoints = () => : "https://romeonline.io" }); -exports.default = InstanceManager; +module.exports = { + InstanceManager, + InstanceRegion, + InstanceIsLocalStackEnabled, + InstanceLocalStackEndpoint, + InstanceEndpoints, + default: InstanceManager +}; diff --git a/server/utils/logger.js b/server/utils/logger.js index 9b4bea937..cf4391890 100644 --- a/server/utils/logger.js +++ b/server/utils/logger.js @@ -2,10 +2,9 @@ const InstanceManager = require("../utils/instanceMgr").default; const winston = require("winston"); const WinstonCloudWatch = require("winston-cloudwatch"); -const { isString, isEmpty } = require("lodash"); const { uploadFileToS3 } = require("./s3"); const { v4 } = require("uuid"); -const { InstanceRegion } = require("./instanceMgr"); +const { InstanceRegion, InstanceIsLocalStackEnabled, InstanceLocalStackEndpoint } = require("./instanceMgr"); const getHostNameOrIP = require("./getHostNameOrIP"); const client = require("../graphql-client/graphql-client").client; const queries = require("../graphql-client/queries"); @@ -48,7 +47,7 @@ const normalizeLevel = (level) => (level ? level.toLowerCase() : LOG_LEVELS.debu const createLogger = () => { try { - const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME); + const isLocal = InstanceIsLocalStackEnabled(); const logGroupName = isLocal ? "development" : process.env.CLOUDWATCH_LOG_GROUP; const winstonCloudwatchTransportDefaults = { @@ -60,7 +59,7 @@ const createLogger = () => { }; if (isLocal) { - winstonCloudwatchTransportDefaults.awsOptions.endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:4566`; + winstonCloudwatchTransportDefaults.awsOptions.endpoint = InstanceLocalStackEndpoint(); } const levelFilter = (levels) => { diff --git a/server/utils/s3.js b/server/utils/s3.js index 2ba1f0d47..bc6f82373 100644 --- a/server/utils/s3.js +++ b/server/utils/s3.js @@ -7,8 +7,7 @@ const { CopyObjectCommand } = require("@aws-sdk/client-s3"); const { defaultProvider } = require("@aws-sdk/credential-provider-node"); -const { InstanceRegion } = require("./instanceMgr"); -const { isString, isEmpty } = require("lodash"); +const { InstanceRegion, InstanceIsLocalStackEnabled, InstanceLocalStackEndpoint } = require("./instanceMgr"); const { getSignedUrl } = require("@aws-sdk/s3-request-presigner"); const createS3Client = () => { @@ -17,10 +16,8 @@ const createS3Client = () => { credentials: defaultProvider() }; - const isLocal = isString(process.env?.LOCALSTACK_HOSTNAME) && !isEmpty(process.env?.LOCALSTACK_HOSTNAME); - - if (isLocal) { - S3Options.endpoint = `http://${process.env.LOCALSTACK_HOSTNAME}:4566`; + if (InstanceIsLocalStackEnabled()) { + S3Options.endpoint = InstanceLocalStackEndpoint(); S3Options.forcePathStyle = true; // Needed for LocalStack to avoid bucket name as hostname } @@ -105,7 +102,7 @@ const createS3Client = () => { }); const presignedUrl = await getSignedUrl(s3Client, command, { expiresIn: 360 }); return presignedUrl; - } + }; return { uploadFileToS3, @@ -119,7 +116,4 @@ const createS3Client = () => { }; }; - - - module.exports = createS3Client(); From 9a86a337bb1acfa414665c27b012bcce520edf08 Mon Sep 17 00:00:00 2001 From: Allan Carr Date: Thu, 2 Apr 2026 15:50:56 -0700 Subject: [PATCH 2/2] IO-3637 DMS ID Production Board Column Signed-off-by: Allan Carr --- .../production-list-columns.add.component.jsx | 1 + .../production-list-columns.data.jsx | 14 +++++++++++++- .../production-list-config-manager.component.jsx | 2 ++ client/src/graphql/jobs.queries.js | 3 +++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/client/src/components/production-list-columns/production-list-columns.add.component.jsx b/client/src/components/production-list-columns/production-list-columns.add.component.jsx index c75834363..a88b2a474 100644 --- a/client/src/components/production-list-columns/production-list-columns.add.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.add.component.jsx @@ -58,6 +58,7 @@ export function ProductionColumnsComponent({ const columnKeys = columns.map((i) => i.key); const cols = dataSource({ + bodyshop, technician, data, state: tableState, diff --git a/client/src/components/production-list-columns/production-list-columns.data.jsx b/client/src/components/production-list-columns/production-list-columns.data.jsx index f44bbb3cc..ff7b4c517 100644 --- a/client/src/components/production-list-columns/production-list-columns.data.jsx +++ b/client/src/components/production-list-columns/production-list-columns.data.jsx @@ -609,7 +609,19 @@ const productionListColumnsData = ({ technician, state, activeStatuses, data, bo ellipsis: true, render: (text, record) => {record.date_repairstarted} - } + }, + ...(bodyshop && bodyshop.rr_dealerid + ? [ + { + title: i18n.t("jobs.fields.dms.id"), + dataIndex: "dms_id", + key: "dms_id", + ellipsis: true, + sorter: (a, b) => alphaSort(a.dms_id, b.dms_id), + sortOrder: state.sortedInfo.columnKey === "dms_id" && state.sortedInfo.order + } + ] + : []), ]; }; export default productionListColumnsData; diff --git a/client/src/components/production-list-table/production-list-config-manager.component.jsx b/client/src/components/production-list-table/production-list-config-manager.component.jsx index 75e15b75d..a061551a3 100644 --- a/client/src/components/production-list-table/production-list-config-manager.component.jsx +++ b/client/src/components/production-list-table/production-list-config-manager.component.jsx @@ -244,6 +244,7 @@ export function ProductionListConfigManager({ nextConfig.columns.columnKeys.map((k) => { return { ...ProductionListColumns({ + bodyshop, technician, state: ensureDefaultState(state), refetch, @@ -270,6 +271,7 @@ export function ProductionListConfigManager({ activeConfig.columns.columnKeys.map((k) => { return { ...ProductionListColumns({ + bodyshop, technician, state: ensureDefaultState(state), refetch, diff --git a/client/src/graphql/jobs.queries.js b/client/src/graphql/jobs.queries.js index 35a3a1f26..e12d23da3 100644 --- a/client/src/graphql/jobs.queries.js +++ b/client/src/graphql/jobs.queries.js @@ -197,6 +197,7 @@ export const QUERY_EXACT_JOB_IN_PRODUCTION = gql` employee_prep employee_csr date_repairstarted + dms_id joblines_status { part_type status @@ -269,6 +270,7 @@ export const QUERY_EXACT_JOBS_IN_PRODUCTION = gql` employee_prep employee_csr date_repairstarted + dms_id joblines_status { part_type status @@ -2671,6 +2673,7 @@ export const QUERY_JOBS_IN_PRODUCTION = gql` suspended job_totals date_repairstarted + dms_id joblines_status { part_type status