feature/IO-3357-Reynolds-and-Reynolds-DMS-API-Integration - Checkpoint

This commit is contained in:
Dave
2025-10-14 13:23:32 -04:00
parent 6bab792b5e
commit 5a9381ebdb
11 changed files with 754 additions and 911 deletions

View File

@@ -12,17 +12,20 @@ const axios = require("axios");
const mustache = require("mustache");
const { XMLParser } = require("fast-xml-parser");
const RRLogger = require("./rr-logger");
const { RR_ACTIONS, RR_SOAP_HEADERS, RR_STAR_SOAP_ACTION, RR_NS, getBaseRRConfig } = require("./rr-constants");
const {
RR_ACTIONS,
RR_SOAP_HEADERS,
RR_SOAP_ACTION,
RR_NS,
getBaseRRConfig,
normalizeRRDealerFields
} = require("./rr-constants");
const { RrApiError } = require("./rr-error");
const xmlFormatter = require("xml-formatter");
/**
* Remove XML decl, collapse inter-tag whitespace, strip empty lines,
* then pretty-print. Safe for XML because we only touch whitespace
* BETWEEN tags, not inside text nodes.
/**
* Collapse Mustache-induced whitespace and pretty print.
* - strips XML decl (inner)
* - strips inner XML decl
* - removes lines that are only whitespace
* - collapses inter-tag whitespace
* - formats with consistent indentation
@@ -69,25 +72,37 @@ async function renderXmlTemplate(templateName, data) {
return rendered.replace(/^\s*<\?xml[^>]*\?>\s*/i, "");
}
// ---------- Config resolution (STAR only) ----------
/**
* Resolve RR config for STAR transport.
*
* Policy:
* - Base (transport) settings (baseUrl, username, password, ppsysId, wssePasswordType, timeout) come from env.
* - Dealer identifiers (dealer/store/branch) MUST be provided by the caller (DB-driven).
* - We DO NOT fall back to env for dealer/store/branch here. Only rr-test.js is allowed to do that.
*/
async function resolveRRConfig(_socket, bodyshopConfig) {
const envCfg = getBaseRRConfig();
const baseEnv = getBaseRRConfig();
if (bodyshopConfig && typeof bodyshopConfig === "object") {
return {
...envCfg,
baseUrl: bodyshopConfig.baseUrl || envCfg.baseUrl,
username: bodyshopConfig.username || envCfg.username,
password: bodyshopConfig.password || envCfg.password,
ppsysId: bodyshopConfig.ppsysId || envCfg.ppsysId,
dealerNumber: bodyshopConfig.dealer_number || envCfg.dealerNumber,
storeNumber: bodyshopConfig.store_number || envCfg.storeNumber,
branchNumber: bodyshopConfig.branch_number || envCfg.branchNumber,
wssePasswordType: bodyshopConfig.wssePasswordType || envCfg.wssePasswordType || "Text",
timeout: envCfg.timeout
};
const { dealerNumber, storeNumber, branchNumber } = normalizeRRDealerFields(bodyshopConfig || {});
if (!dealerNumber || !storeNumber || !branchNumber) {
throw new Error(
"Missing dealer/store/branch in RR config. These must be loaded from the database (no env fallback here)."
);
}
return envCfg;
return {
baseUrl: bodyshopConfig?.baseUrl || baseEnv.baseUrl,
username: bodyshopConfig?.username || baseEnv.username,
password: bodyshopConfig?.password || baseEnv.password,
ppsysId: bodyshopConfig?.ppsysId || baseEnv.ppsysId,
wssePasswordType: bodyshopConfig?.wssePasswordType || baseEnv.wssePasswordType || "Text",
timeout: baseEnv.timeout,
// canonical identifiers (DB-driven only)
dealerNumber,
storeNumber,
branchNumber
};
}
// ---------- Response parsing ----------
@@ -161,8 +176,7 @@ function parseRRResponse(xml) {
// ---------- STAR envelope helpers ----------
function wrapWithApplicationArea(innerXml, { CreationDateTime, BODId, Sender, Destination }) {
// Make sure we inject *inside* the STAR root, not before it.
// 1) Strip any XML declaration just in case (idempotent)
// Strip any inner XML declaration (idempotent)
let xml = innerXml.replace(/^\s*<\?xml[^>]*\?>\s*/i, "");
const appArea = `
@@ -182,8 +196,7 @@ function wrapWithApplicationArea(innerXml, { CreationDateTime, BODId, Sender, De
</Destination>
</ApplicationArea>`.trim();
// Inject right after the opening tag of the root element (skip processing instructions)
// e.g. <rey_RomeGetAdvisorsReq ...> ==> insert ApplicationArea here
// Inject right after the opening tag of the root element
xml = xml.replace(/^(\s*<[^!?][^>]*>)/, `$1\n${appArea}\n`);
return xml;
@@ -229,7 +242,7 @@ async function MakeRRCall({
action,
body,
socket,
dealerConfig, // optional per-shop overrides
dealerConfig, // required in runtime code; rr-test.js can still pass env-inflated cfg
retries = 1,
jobid
}) {
@@ -237,6 +250,7 @@ async function MakeRRCall({
throw new Error(`Invalid RR action: ${action}`);
}
// Prefer explicit dealerConfig from caller; otherwise enforce DB-provided config via resolveRRConfig
const cfg = dealerConfig || (await resolveRRConfig(socket, undefined));
const baseUrl = cfg.baseUrl;
if (!baseUrl) throw new Error("Missing RR base URL");
@@ -246,8 +260,7 @@ async function MakeRRCall({
const renderedBusiness = await renderXmlTemplate(templateName, body?.data || {});
// Build STAR envelope
let envelope = await buildStarEnvelope(renderedBusiness, cfg, body?.appArea);
const envelope = await buildStarEnvelope(renderedBusiness, cfg, body?.appArea);
const formattedEnvelope = prettyPrintXml(envelope);
// Guardrails
@@ -255,11 +268,11 @@ async function MakeRRCall({
throw new Error("STAR envelope malformed: missing ProcessMessage/ApplicationArea");
}
const headers = { ...RR_SOAP_HEADERS, SOAPAction: RR_STAR_SOAP_ACTION };
const headers = { ...RR_SOAP_HEADERS, SOAPAction: RR_SOAP_ACTION };
RRLogger(socket, "debug", `Sending RR SOAP request`, {
action,
soapAction: RR_STAR_SOAP_ACTION,
soapAction: RR_SOAP_ACTION,
endpoint: baseUrl,
jobid,
mode: "STAR"