release/2026-02-27 - Add gating

This commit is contained in:
Dave
2026-03-03 15:25:13 -05:00
parent 3f2358e30c
commit b91e65be0e
2 changed files with 76 additions and 12 deletions

View File

@@ -20,7 +20,8 @@ export function DmsLogEvents({
detailsNonce,
isDarkMode,
colorizeJson = false,
showDetails = true
showDetails = true,
allowXmlPayload = true
}) {
const { t } = useTranslation();
const [openSet, setOpenSet] = useState(() => new Set());
@@ -75,7 +76,7 @@ export function DmsLogEvents({
// Only treat meta as "present" when we are allowed to show details
const hasMeta = !isEmpty(meta) && showDetails;
const isOpen = hasMeta && openSet.has(idx);
const xml = hasMeta ? extractXmlFromMeta(meta) : { request: null, response: null };
const xml = hasMeta && allowXmlPayload ? extractXmlFromMeta(meta) : { request: null, response: null };
const hasRequestXml = !!xml.request;
const hasResponseXml = !!xml.response;
const copyPayload = hasMeta ? getCopyPayload(meta) : null;
@@ -175,7 +176,7 @@ export function DmsLogEvents({
)
};
}),
[logs, openSet, colorizeJson, copiedKey, isDarkMode, showDetails, t]
[logs, openSet, colorizeJson, copiedKey, isDarkMode, showDetails, allowXmlPayload, t]
);
return <Timeline reverse items={items} />;

View File

@@ -11,7 +11,7 @@ import { useSocket } from "../../contexts/SocketIO/useSocket.js";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
import { QUERY_JOB_EXPORT_DMS } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import { insertAuditTrail, setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
@@ -29,7 +29,8 @@ import DmsAllocationsSummary from "../../components/dms-allocations-summary/dms-
import RrAllocationsSummary from "../../components/dms-allocations-summary/rr-dms-allocations-summary.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
bodyshop: selectBodyshop,
currentUser: selectCurrentUser
});
const mapDispatchToProps = (dispatch) => ({
@@ -65,7 +66,41 @@ const DMS_SOCKET_EVENTS = {
}
};
export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, insertAuditTrail }) {
const stripRrXmlFromPayload = (input) => {
if (input == null || typeof input !== "object") return input;
let target = null;
try {
target = JSON.parse(JSON.stringify(input));
} catch {
// Fallback to in-place scrub if cloning fails.
target = input;
}
const scrub = (node) => {
if (node == null || typeof node !== "object") return;
if (Array.isArray(node)) {
node.forEach(scrub);
return;
}
delete node.requestXml;
delete node.responseXml;
if (node.xml && typeof node.xml === "object") {
delete node.xml.request;
delete node.xml.response;
if (Object.keys(node.xml).length === 0) delete node.xml;
}
Object.values(node).forEach(scrub);
};
scrub(target);
return target;
};
export function DmsContainer({ bodyshop, currentUser, setBreadcrumbs, setSelectedHeader, insertAuditTrail }) {
const {
treatments: { Fortellis }
} = useTreatmentsWithConfig({
@@ -79,6 +114,15 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
const [allocationsSummary, setAllocationsSummary] = useState(null);
const [reconnectNonce, setReconnectNonce] = useState(0);
const isDevEnv = import.meta.env.DEV;
const isProdEnv = import.meta.env.PROD;
const userEmail = (currentUser?.email || "").toLowerCase();
const devEmails = ["imex.dev", "rome.dev"];
const prodEmails = ["imex.prod", "rome.prod", "imex.test", "rome.test"];
const hasValidEmail = (emails) => emails.some((email) => userEmail.endsWith(email));
const canViewSensitiveRrXml = (isDevEnv && hasValidEmail(devEmails)) || (isProdEnv && hasValidEmail(prodEmails));
// Compute a single normalized mode and pick the proper socket
const mode = getDmsMode(bodyshop, Fortellis.treatment); // "rr" | "fortellis" | "cdk" | "pbs" | "none"
@@ -241,6 +285,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
}, [jobId, mode, activeSocket]);
const handleExportFailed = (payload = {}) => {
const safePayload = canViewSensitiveRrXml ? payload : stripRrXmlFromPayload(payload);
const { title, friendlyMessage, error: errText, severity, errorCode, vendorStatusCode } = payload;
const msg =
@@ -271,7 +316,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
timestamp: new Date(),
level: (sev || "error").toUpperCase(),
message: `${vendorTitle}: ${msg}`,
meta: { errorCode, vendorStatusCode, raw: payload, blockedByOpenRoLimit: !!isRrOpenRoLimit }
meta: { errorCode, vendorStatusCode, raw: safePayload, blockedByOpenRoLimit: !!isRrOpenRoLimit }
}
]);
};
@@ -344,11 +389,16 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
// Logs
const onLog = isRrMode
? (payload = {}) => {
const safePayload = canViewSensitiveRrXml ? payload : stripRrXmlFromPayload(payload);
const normalized = {
timestamp: payload.timestamp ? new Date(payload.timestamp) : payload.ts ? new Date(payload.ts) : new Date(),
level: (payload.level || "INFO").toUpperCase(),
message: payload.message || payload.msg || "",
meta: payload.meta ?? payload.ctx ?? payload.details ?? null
timestamp: safePayload.timestamp
? new Date(safePayload.timestamp)
: safePayload.ts
? new Date(safePayload.ts)
: new Date(),
level: (safePayload.level || "INFO").toUpperCase(),
message: safePayload.message || safePayload.msg || "",
meta: safePayload.meta ?? safePayload.ctx ?? safePayload.details ?? null
};
setLogs((prev) => [...prev, normalized]);
}
@@ -429,7 +479,19 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
activeSocket.disconnect();
}
};
}, [mode, activeSocket, channels, logLevel, notification, t, insertAuditTrail, history, isRrMode, providerLabel]);
}, [
mode,
activeSocket,
channels,
logLevel,
notification,
t,
insertAuditTrail,
history,
isRrMode,
providerLabel,
canViewSensitiveRrXml
]);
// RR finalize callback (unchanged public behavior)
const handleRrValidationFinished = () => {
@@ -588,6 +650,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
detailsNonce={detailsNonce}
colorizeJson={isRrMode ? colorizeJson : false}
showDetails={isRrMode}
allowXmlPayload={canViewSensitiveRrXml}
/>
</Card>
</div>