266 lines
7.5 KiB
JavaScript
266 lines
7.5 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* RR smoke test / CLI (STAR-only)
|
||
*/
|
||
|
||
const path = require("path");
|
||
const fs = require("fs");
|
||
const dotenv = require("dotenv");
|
||
const { GraphQLClient, gql } = require("graphql-request");
|
||
const { MakeRRCall, renderXmlTemplate, buildStarEnvelope } = require("./rr-helpers");
|
||
const { getBaseRRConfig } = require("./rr-constants");
|
||
|
||
// Load env file for local runs
|
||
const defaultEnvPath = path.resolve(__dirname, "../../.env.development");
|
||
if (fs.existsSync(defaultEnvPath)) {
|
||
const result = dotenv.config({ path: defaultEnvPath });
|
||
if (result?.parsed) {
|
||
console.log(
|
||
`${defaultEnvPath}\n[dotenv@${require("dotenv/package.json").version}] injecting env (${Object.keys(result.parsed).length}) from ../../.env.development`
|
||
);
|
||
}
|
||
}
|
||
|
||
// ---- CLI args parsing ----
|
||
const argv = process.argv.slice(2);
|
||
const args = { _: [] };
|
||
for (let i = 0; i < argv.length; i++) {
|
||
const a = argv[i];
|
||
|
||
if (a.startsWith("--")) {
|
||
const eq = a.indexOf("=");
|
||
if (eq > -1) {
|
||
const k = a.slice(2, eq);
|
||
const v = a.slice(eq + 1);
|
||
args[k] = v;
|
||
} else {
|
||
const k = a.slice(2);
|
||
const next = argv[i + 1];
|
||
if (next && !next.startsWith("-")) {
|
||
args[k] = next;
|
||
i++;
|
||
} else {
|
||
args[k] = true;
|
||
}
|
||
}
|
||
} else if (a.startsWith("-") && a.length > 1) {
|
||
const k = a.slice(1);
|
||
const next = argv[i + 1];
|
||
if (next && !next.startsWith("-")) {
|
||
args[k] = next;
|
||
i++;
|
||
} else {
|
||
args[k] = true;
|
||
}
|
||
} else {
|
||
args._.push(a);
|
||
}
|
||
}
|
||
|
||
function toIntOr(defaultVal, maybe) {
|
||
const n = parseInt(maybe, 10);
|
||
return Number.isFinite(n) ? n : defaultVal;
|
||
}
|
||
|
||
// ---------------- GraphQL helpers ----------------
|
||
|
||
function buildGqlClient() {
|
||
const endpoint = process.env.GRAPHQL_ENDPOINT;
|
||
if (!endpoint) throw new Error("GRAPHQL_ENDPOINT env var is required when using --bodyshopId.");
|
||
|
||
const headers = {};
|
||
if (process.env.HASURA_ADMIN_SECRET) {
|
||
headers["x-hasura-admin-secret"] = process.env.HASURA_ADMIN_SECRET;
|
||
} else if (process.env.GRAPHQL_BEARER) {
|
||
headers["authorization"] = `Bearer ${process.env.GRAPHQL_BEARER}`;
|
||
}
|
||
|
||
return new GraphQLClient(endpoint, { headers });
|
||
}
|
||
|
||
const Q_BODYSHOPS_BY_PK = gql`
|
||
query BodyshopRR($id: uuid!) {
|
||
bodyshops_by_pk(id: $id) {
|
||
rr_dealerid
|
||
rr_configuration
|
||
}
|
||
}
|
||
`;
|
||
|
||
function normalizeConfigRecord(rec) {
|
||
if (!rec) return null;
|
||
let cfg = rec.rr_configuration || {};
|
||
if (typeof cfg === "string") {
|
||
try {
|
||
cfg = JSON.parse(cfg);
|
||
} catch {
|
||
cfg = {};
|
||
}
|
||
}
|
||
return {
|
||
dealerNumber: rec.rr_dealerid || undefined,
|
||
storeNumber: cfg.storeNumber || undefined,
|
||
branchNumber: cfg.branchNumber || undefined
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Load RR config overrides from DB (bodyshops_by_pk only).
|
||
*/
|
||
async function loadBodyshopRRConfig(bodyshopId) {
|
||
if (!bodyshopId) return null;
|
||
|
||
const client = buildGqlClient();
|
||
const { bodyshops_by_pk: bs } = await client.request(Q_BODYSHOPS_BY_PK, { id: bodyshopId });
|
||
|
||
if (!bs) throw new Error("Bodyshop not found.");
|
||
if (!bs.rr_dealerid) throw new Error("Bodyshop is not configured for RR (missing rr_dealerid).");
|
||
|
||
return normalizeConfigRecord(bs);
|
||
}
|
||
|
||
// ---------------- rr-test logic ----------------
|
||
|
||
function pickActionName(raw) {
|
||
if (!raw || typeof raw !== "string") return "ping";
|
||
const x = raw.toLowerCase();
|
||
if (x === "combined" || x === "combinedsearch" || x === "comb") return "combined";
|
||
if (x === "advisors" || x === "advisor" || x === "getadvisors") return "advisors";
|
||
if (x === "parts" || x === "getparts" || x === "part") return "parts";
|
||
if (x === "ping") return "ping";
|
||
return x;
|
||
}
|
||
|
||
function buildBodyForAction(action, args, cfg) {
|
||
switch (action) {
|
||
case "ping":
|
||
case "advisors": {
|
||
const max = toIntOr(1, args.max);
|
||
const data = {
|
||
DealerCode: cfg.dealerNumber,
|
||
DealerNumber: cfg.dealerNumber,
|
||
StoreNumber: cfg.storeNumber,
|
||
BranchNumber: cfg.branchNumber,
|
||
SearchCriteria: {
|
||
AdvisorId: args.advisorId,
|
||
FirstName: args.first || args.firstname,
|
||
LastName: args.last || args.lastname,
|
||
Department: args.department,
|
||
Status: args.status || "ACTIVE",
|
||
IncludeInactive: args.includeInactive ? "true" : undefined,
|
||
MaxResults: max
|
||
}
|
||
};
|
||
return { template: "GetAdvisors", data, appArea: {} };
|
||
}
|
||
|
||
case "combined": {
|
||
const max = toIntOr(10, args.max);
|
||
const data = {
|
||
DealerNumber: cfg.dealerNumber,
|
||
StoreNumber: cfg.storeNumber,
|
||
BranchNumber: cfg.branchNumber,
|
||
Customer: {
|
||
FirstName: args.first,
|
||
LastName: args.last,
|
||
PhoneNumber: args.phone,
|
||
EmailAddress: args.email
|
||
},
|
||
Vehicle: {
|
||
VIN: args.vin,
|
||
LicensePlate: args.plate
|
||
},
|
||
MaxResults: max
|
||
};
|
||
return { template: "CombinedSearch", data, appArea: {} };
|
||
}
|
||
|
||
case "parts": {
|
||
const max = toIntOr(5, args.max);
|
||
const data = {
|
||
DealerNumber: cfg.dealerNumber,
|
||
StoreNumber: cfg.storeNumber,
|
||
BranchNumber: cfg.branchNumber,
|
||
SearchCriteria: {
|
||
PartNumber: args.part,
|
||
Description: args.desc,
|
||
Make: args.make,
|
||
Model: args.model,
|
||
Year: args.year,
|
||
MaxResults: max
|
||
}
|
||
};
|
||
return { template: "GetParts", data, appArea: {} };
|
||
}
|
||
|
||
default:
|
||
throw new Error(`Unsupported action: ${action}`);
|
||
}
|
||
}
|
||
|
||
async function main() {
|
||
const action = pickActionName(args.action || args.a || args._[0]);
|
||
const bodyshopId = args.bodyshopId || args.bodyshop || args.b;
|
||
|
||
const rrAction =
|
||
action === "ping"
|
||
? "GetAdvisors"
|
||
: action === "advisors"
|
||
? "GetAdvisors"
|
||
: action === "combined"
|
||
? "CombinedSearch"
|
||
: action === "parts"
|
||
? "GetParts"
|
||
: action;
|
||
|
||
// Start with env-based defaults…
|
||
const cfg = getBaseRRConfig();
|
||
|
||
// …then override with per-bodyshop values if provided.
|
||
if (bodyshopId) {
|
||
try {
|
||
const overrides = await loadBodyshopRRConfig(bodyshopId);
|
||
if (overrides?.dealerNumber) cfg.dealerNumber = overrides.dealerNumber;
|
||
if (overrides?.storeNumber) cfg.storeNumber = overrides.storeNumber;
|
||
if (overrides?.branchNumber) cfg.branchNumber = overrides.branchNumber;
|
||
console.log("ℹ️ RR config loaded from DB via: bodyshops_by_pk");
|
||
} catch (e) {
|
||
console.error("❌ Failed to load per-bodyshop RR config:", e.message);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
const body = buildBodyForAction(action, args, cfg);
|
||
const templateName = body.template || rrAction;
|
||
|
||
try {
|
||
await renderXmlTemplate(templateName, body.data);
|
||
console.log("✅ Templates verified.");
|
||
} catch (e) {
|
||
console.error("❌ Template verification failed:", e.message);
|
||
process.exit(1);
|
||
}
|
||
|
||
if (args.dry) {
|
||
const business = await renderXmlTemplate(templateName, body.data);
|
||
const envelope = await buildStarEnvelope(business, cfg, body.appArea);
|
||
console.log("\n--- FULL SOAP ENVELOPE ---\n");
|
||
console.log(envelope);
|
||
console.log("\n(dry run) 🚫 Skipping network call.");
|
||
return;
|
||
}
|
||
|
||
try {
|
||
console.log(`\n▶ Calling Rome action: ${rrAction}`);
|
||
const xml = await MakeRRCall({ action: rrAction, body, dealerConfig: cfg });
|
||
console.log("\n✅ RR call succeeded.\n");
|
||
console.log(xml);
|
||
} catch (err) {
|
||
console.dir(err);
|
||
console.error("[RR] rr-test failed", { message: err.message, stack: err.stack });
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
main().catch(() => process.exit(1));
|