Implemented the customer installed-base view

This commit is contained in:
2026-09-22 16:30:07 -03:00
parent 8d73954362
commit 3ee8eb6f21
22 changed files with 1102 additions and 19 deletions

View File

@@ -11,6 +11,7 @@ function createMessageTypes() {
opptyProductsRequestDebug: "opportunitiesExtension.opptyProductsRequestDebug",
requestConsumerTokenService: "opportunitiesExtension.requestConsumerTokenService",
requestAccountPlans: "opportunitiesExtension.requestAccountPlans",
requestInstallBase: "opportunitiesExtension.requestInstallBase",
requestConsumptionTrend: "opportunitiesExtension.requestConsumptionTrend"
});
}
@@ -156,7 +157,8 @@ function createAuthConsumerSession({
ok: true,
authorizations: {
accountSummary: accountSummaryAuthorization,
accountPlans: accountPlansAuthorization
accountPlans: accountPlansAuthorization,
installBase: extractTokenServiceAuthorization(tokenResponse, "SAIB-TS", "")
},
debugRequests
};
@@ -438,6 +440,68 @@ function createSpaAccounts({
return { requestAccountPlans };
}
// Source: src/services/spa/install-base.js
function createSpaInstallBase({ ACCOUNT_SUMMARY_URL, fetchJson, findNestedObject, normalizeMessageAuthorization }) {
const endpoint = "https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-installbase-provider/service/account/";
async function requestInstallBase(message) {
const debugRequests = [];
try {
const partyNumber = String(message.partyNumber || "").trim();
const summaryAuth = normalizeMessageAuthorization(message.authorizations?.accountSummary, "account-ti-authorization");
const installAuth = normalizeMessageAuthorization(message.authorizations?.installBase, "");
if (!partyNumber) throw new Error("Customer Party Number is required.");
if (!summaryAuth.value) throw new Error("ACCP-TS authorization is unavailable.");
if (!installAuth.name || !installAuth.value) throw new Error("SAIB-TS uiaasHeader.label/value is unavailable.");
const summary = await fetchJson(ACCOUNT_SUMMARY_URL, {
method: "POST", credentials: "include",
headers: { Accept: "application/json", "Content-Type": "application/json", [summaryAuth.name]: summaryAuth.value },
body: JSON.stringify([{ key: "regId", value: partyNumber }])
}, "requestAccountSummary", debugRequests);
const account = findNestedObject(summary, value =>
(typeof value.registryId === "string" || typeof value.registryId === "number") && String(value.registryId).trim() !== "");
if (!account) throw new Error("registryId was not found in requestAccountSummary.");
const payload = {
text: "", accountHierarchy: "single", accountScope: "all", productScope: "all",
installStatus: ["active", "inactive", "unsupported"], productIds: null,
contractEndDate: { to: null, from: null }, contractStartDate: { to: null, from: null },
purchaseStartDate: { to: null, from: null }, registryIds: [], gecPartyNumbers: null
};
const items = [];
let offset = 0;
// Bound malformed pagination without silently returning a partial result.
for (let page = 0; page < 1000; page += 1) {
const url = `${endpoint}${encodeURIComponent(String(account.registryId).trim())}?orderBy=currentSupport%3Adesc&limit=25&offset=${offset}`;
const result = await fetchJson(url, {
method: "POST", credentials: "include",
headers: { Accept: "application/json", "Content-Type": "application/json", [installAuth.name]: installAuth.value },
body: JSON.stringify(payload)
}, "requestInstallBase", debugRequests);
if (!result || !Array.isArray(result.items) || typeof result.hasMore !== "boolean"
|| result.items.some(item => !item || typeof item !== "object" || Array.isArray(item))) {
throw new Error("requestInstallBase returned an invalid collection.");
}
if (result.offset !== undefined && result.offset !== offset) throw new Error("requestInstallBase returned an unexpected offset.");
items.push(...result.items);
if (!result.hasMore) return { ok: true, items, debugRequests };
if (!result.items.length) throw new Error("requestInstallBase pagination made no progress.");
offset += result.items.length;
}
throw new Error("requestInstallBase pagination limit reached.");
} catch (error) {
error.debugRequests = debugRequests;
throw error;
} finally {
// The provider chooses the credential header name; never rely on its spelling for redaction.
for (const entry of debugRequests) {
for (const key of Object.keys(entry.requestHeaders || {})) {
if (!/^(accept|content-type)$/i.test(key)) entry.requestHeaders[key] = "(present)";
}
}
}
}
return { requestInstallBase };
}
// Source: src/services/spa/consumption.js
function createSpaConsumption({
CONSUMPTION_TREND_REQUESTS,
@@ -1448,6 +1512,7 @@ function registerBackgroundMessages({
getXsrfTokenCookie,
refreshXsrfCookie,
requestAccountPlans,
requestInstallBase,
requestConsumerTokenService,
requestConsumptionTrend,
requestOpptyProducts,
@@ -1527,6 +1592,14 @@ runtimeApi.runtime.onMessage.addListener((message, sender, sendResponse) => {
return true;
}
if (message.type === messageTypes.requestInstallBase) {
requestInstallBase(message).then(sendResponse).catch(error => sendResponse({
ok: false, error: error.message || "Unable to load installed base.",
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
}));
return true;
}
if (message.type === messageTypes.requestAccountPlans) {
requestAccountPlans(message)
.then(sendResponse)
@@ -1596,6 +1669,12 @@ function createBackgroundApplication(overrides = {}) {
findAccountPlanId: (...args) => spaResponseShapes.findAccountPlanId(...args),
normalizeMessageAuthorization: (...args) => authSpaCredentials.normalizeMessageAuthorization(...args)
});
const spaInstallBase = createSpaInstallBase({
ACCOUNT_SUMMARY_URL: contracts.ACCOUNT_SUMMARY_URL,
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
findNestedObject: (...args) => spaResponseShapes.findNestedObject(...args),
normalizeMessageAuthorization: (...args) => authSpaCredentials.normalizeMessageAuthorization(...args)
});
const spaConsumption = createSpaConsumption({
CONSUMPTION_TREND_REQUESTS: contracts.CONSUMPTION_TREND_REQUESTS,
CONSUMPTION_TREND_URL: contracts.CONSUMPTION_TREND_URL,
@@ -1659,12 +1738,13 @@ function createBackgroundApplication(overrides = {}) {
const getXsrfTokenCookie = (...args) => authXsrfCookies.getXsrfTokenCookie(...args);
const refreshXsrfCookie = (...args) => authXsrfRecovery.refreshXsrfCookie(...args);
const requestAccountPlans = (...args) => spaAccounts.requestAccountPlans(...args);
const requestInstallBase = (...args) => spaInstallBase.requestInstallBase(...args);
const requestConsumerTokenService = (...args) => authConsumerSession.requestConsumerTokenService(...args);
const requestConsumptionTrend = (...args) => spaConsumption.requestConsumptionTrend(...args);
const requestOpptyProducts = (...args) => productsBackgroundJobs.requestOpptyProducts(...args);
const runtimeApi = environment.browser || environment.chrome;
function start() {
startBackgroundApplication({ messageTypes, getXsrfTokenCookie, refreshXsrfCookie, requestAccountPlans, requestConsumerTokenService, requestConsumptionTrend, requestOpptyProducts, runtimeApi });
startBackgroundApplication({ messageTypes, getXsrfTokenCookie, refreshXsrfCookie, requestAccountPlans, requestInstallBase, requestConsumerTokenService, requestConsumptionTrend, requestOpptyProducts, runtimeApi });
}
return {
start, contracts,
@@ -1672,6 +1752,7 @@ function createBackgroundApplication(overrides = {}) {
authConsumerSession,
authSpaCredentials,
spaAccounts,
spaInstallBase,
spaConsumption,
spaResponseShapes,
httpBackgroundJson,