release/2026-02-27 - Add gating
This commit is contained in:
@@ -20,7 +20,8 @@ export function DmsLogEvents({
|
|||||||
detailsNonce,
|
detailsNonce,
|
||||||
isDarkMode,
|
isDarkMode,
|
||||||
colorizeJson = false,
|
colorizeJson = false,
|
||||||
showDetails = true
|
showDetails = true,
|
||||||
|
allowXmlPayload = true
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [openSet, setOpenSet] = useState(() => new Set());
|
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
|
// Only treat meta as "present" when we are allowed to show details
|
||||||
const hasMeta = !isEmpty(meta) && showDetails;
|
const hasMeta = !isEmpty(meta) && showDetails;
|
||||||
const isOpen = hasMeta && openSet.has(idx);
|
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 hasRequestXml = !!xml.request;
|
||||||
const hasResponseXml = !!xml.response;
|
const hasResponseXml = !!xml.response;
|
||||||
const copyPayload = hasMeta ? getCopyPayload(meta) : null;
|
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} />;
|
return <Timeline reverse items={items} />;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { useSocket } from "../../contexts/SocketIO/useSocket.js";
|
|||||||
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
|
||||||
|
|
||||||
import { QUERY_JOB_EXPORT_DMS } from "../../graphql/jobs.queries";
|
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 { insertAuditTrail, setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
|
||||||
|
|
||||||
import InstanceRenderManager from "../../utils/instanceRenderMgr";
|
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";
|
import RrAllocationsSummary from "../../components/dms-allocations-summary/rr-dms-allocations-summary.component";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
bodyshop: selectBodyshop
|
bodyshop: selectBodyshop,
|
||||||
|
currentUser: selectCurrentUser
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
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 {
|
const {
|
||||||
treatments: { Fortellis }
|
treatments: { Fortellis }
|
||||||
} = useTreatmentsWithConfig({
|
} = useTreatmentsWithConfig({
|
||||||
@@ -79,6 +114,15 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
|
|||||||
const [allocationsSummary, setAllocationsSummary] = useState(null);
|
const [allocationsSummary, setAllocationsSummary] = useState(null);
|
||||||
const [reconnectNonce, setReconnectNonce] = useState(0);
|
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
|
// Compute a single normalized mode and pick the proper socket
|
||||||
const mode = getDmsMode(bodyshop, Fortellis.treatment); // "rr" | "fortellis" | "cdk" | "pbs" | "none"
|
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]);
|
}, [jobId, mode, activeSocket]);
|
||||||
|
|
||||||
const handleExportFailed = (payload = {}) => {
|
const handleExportFailed = (payload = {}) => {
|
||||||
|
const safePayload = canViewSensitiveRrXml ? payload : stripRrXmlFromPayload(payload);
|
||||||
const { title, friendlyMessage, error: errText, severity, errorCode, vendorStatusCode } = payload;
|
const { title, friendlyMessage, error: errText, severity, errorCode, vendorStatusCode } = payload;
|
||||||
|
|
||||||
const msg =
|
const msg =
|
||||||
@@ -271,7 +316,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
|
|||||||
timestamp: new Date(),
|
timestamp: new Date(),
|
||||||
level: (sev || "error").toUpperCase(),
|
level: (sev || "error").toUpperCase(),
|
||||||
message: `${vendorTitle}: ${msg}`,
|
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
|
// Logs
|
||||||
const onLog = isRrMode
|
const onLog = isRrMode
|
||||||
? (payload = {}) => {
|
? (payload = {}) => {
|
||||||
|
const safePayload = canViewSensitiveRrXml ? payload : stripRrXmlFromPayload(payload);
|
||||||
const normalized = {
|
const normalized = {
|
||||||
timestamp: payload.timestamp ? new Date(payload.timestamp) : payload.ts ? new Date(payload.ts) : new Date(),
|
timestamp: safePayload.timestamp
|
||||||
level: (payload.level || "INFO").toUpperCase(),
|
? new Date(safePayload.timestamp)
|
||||||
message: payload.message || payload.msg || "",
|
: safePayload.ts
|
||||||
meta: payload.meta ?? payload.ctx ?? payload.details ?? null
|
? 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]);
|
setLogs((prev) => [...prev, normalized]);
|
||||||
}
|
}
|
||||||
@@ -429,7 +479,19 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
|
|||||||
activeSocket.disconnect();
|
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)
|
// RR finalize callback (unchanged public behavior)
|
||||||
const handleRrValidationFinished = () => {
|
const handleRrValidationFinished = () => {
|
||||||
@@ -588,6 +650,7 @@ export function DmsContainer({ bodyshop, setBreadcrumbs, setSelectedHeader, inse
|
|||||||
detailsNonce={detailsNonce}
|
detailsNonce={detailsNonce}
|
||||||
colorizeJson={isRrMode ? colorizeJson : false}
|
colorizeJson={isRrMode ? colorizeJson : false}
|
||||||
showDetails={isRrMode}
|
showDetails={isRrMode}
|
||||||
|
allowXmlPayload={canViewSensitiveRrXml}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user