Implemented the customer service requests view
This commit is contained in:
@@ -59,7 +59,8 @@ function createAuthConsumerSession({
|
||||
authorizations: {
|
||||
accountSummary: accountSummaryAuthorization,
|
||||
accountPlans: accountPlansAuthorization,
|
||||
installBase: extractTokenServiceAuthorization(tokenResponse, "SAIB-TS", "")
|
||||
installBase: extractTokenServiceAuthorization(tokenResponse, "SAIB-TS", ""),
|
||||
customerServiceRequests: extractTokenServiceAuthorization(tokenResponse, "SPA-ORANGE", "")
|
||||
},
|
||||
debugRequests
|
||||
};
|
||||
|
||||
@@ -40,6 +40,12 @@ function createBackgroundApplication(overrides = {}) {
|
||||
findNestedObject: (...args) => spaResponseShapes.findNestedObject(...args),
|
||||
normalizeMessageAuthorization: (...args) => authSpaCredentials.normalizeMessageAuthorization(...args)
|
||||
});
|
||||
const spaCustomerServiceRequests = createSpaCustomerServiceRequests({
|
||||
ACCOUNT_SUMMARY_URL: contracts.ACCOUNT_SUMMARY_URL,
|
||||
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
|
||||
findAccountPlanId: (...args) => spaResponseShapes.findAccountPlanId(...args),
|
||||
normalizeMessageAuthorization: (...args) => authSpaCredentials.normalizeMessageAuthorization(...args)
|
||||
});
|
||||
const spaConsumption = createSpaConsumption({
|
||||
CONSUMPTION_TREND_REQUESTS: contracts.CONSUMPTION_TREND_REQUESTS,
|
||||
CONSUMPTION_TREND_URL: contracts.CONSUMPTION_TREND_URL,
|
||||
@@ -104,12 +110,13 @@ function createBackgroundApplication(overrides = {}) {
|
||||
const refreshXsrfCookie = (...args) => authXsrfRecovery.refreshXsrfCookie(...args);
|
||||
const requestAccountPlans = (...args) => spaAccounts.requestAccountPlans(...args);
|
||||
const requestInstallBase = (...args) => spaInstallBase.requestInstallBase(...args);
|
||||
const requestCustomerServiceRequest = (...args) => spaCustomerServiceRequests.requestCustomerServiceRequest(...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, requestInstallBase, requestConsumerTokenService, requestConsumptionTrend, requestOpptyProducts, runtimeApi });
|
||||
startBackgroundApplication({ messageTypes, getXsrfTokenCookie, refreshXsrfCookie, requestAccountPlans, requestInstallBase, requestCustomerServiceRequest, requestConsumerTokenService, requestConsumptionTrend, requestOpptyProducts, runtimeApi });
|
||||
}
|
||||
return {
|
||||
start, contracts,
|
||||
@@ -118,6 +125,7 @@ function createBackgroundApplication(overrides = {}) {
|
||||
authSpaCredentials,
|
||||
spaAccounts,
|
||||
spaInstallBase,
|
||||
spaCustomerServiceRequests,
|
||||
spaConsumption,
|
||||
spaResponseShapes,
|
||||
httpBackgroundJson,
|
||||
|
||||
@@ -5,6 +5,7 @@ function registerBackgroundMessages({
|
||||
refreshXsrfCookie,
|
||||
requestAccountPlans,
|
||||
requestInstallBase,
|
||||
requestCustomerServiceRequest,
|
||||
requestConsumerTokenService,
|
||||
requestConsumptionTrend,
|
||||
requestOpptyProducts,
|
||||
@@ -84,6 +85,14 @@ runtimeApi.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.type === messageTypes.requestCustomerServiceRequest) {
|
||||
requestCustomerServiceRequest(message).then(sendResponse).catch(error => sendResponse({
|
||||
ok: false, error: error.message || "Unable to load service requests.",
|
||||
debugRequests: Array.isArray(error.debugRequests) ? error.debugRequests : []
|
||||
}));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.type === messageTypes.requestInstallBase) {
|
||||
requestInstallBase(message).then(sendResponse).catch(error => sendResponse({
|
||||
ok: false, error: error.message || "Unable to load installed base.",
|
||||
|
||||
@@ -77,6 +77,7 @@ function createContentApplication(overrides = {}) {
|
||||
authSessionState,
|
||||
closeAccountPlansModal: (...args) => consumptionPlansView.closeAccountPlansModal(...args),
|
||||
closeInstallBaseModal: (...args) => installBaseView.closeInstallBaseModal(...args),
|
||||
closeServiceRequestsModal: (...args) => serviceRequestsView.closeServiceRequestsModal(...args),
|
||||
closeCustomerFilterPanel: (...args) => opportunitiesFilters.closeCustomerFilterPanel(...args),
|
||||
closeOwnerFilterPanel: (...args) => opportunitiesFilters.closeOwnerFilterPanel(...args),
|
||||
closePreferencesModal: (...args) => preferencesView.closePreferencesModal(...args),
|
||||
@@ -199,6 +200,7 @@ function createContentApplication(overrides = {}) {
|
||||
OPPORTUNITY_DETAIL_URL: contracts.OPPORTUNITY_DETAIL_URL,
|
||||
createCloudConsumptionButton: (...args) => consumptionPlansAction.createCloudConsumptionButton(...args),
|
||||
createInstallBaseButton: (...args) => installBaseAction.createInstallBaseButton(...args),
|
||||
createServiceRequestsButton: (...args) => serviceRequestsAction.createServiceRequestsButton(...args),
|
||||
createCopyOpportunityButton: (...args) => opportunitiesLinks.createCopyOpportunityButton(...args),
|
||||
createDetailLink: (...args) => opportunitiesLinks.createDetailLink(...args),
|
||||
createProductsCountButton: (...args) => productsDetails.createProductsCountButton(...args),
|
||||
@@ -333,6 +335,20 @@ function createContentApplication(overrides = {}) {
|
||||
openInstallBaseModal: (...args) => installBaseView.openInstallBaseModal(...args),
|
||||
environment
|
||||
});
|
||||
const serviceRequestsView = createServiceRequestsView({
|
||||
MODAL_ID: contracts.MODAL_ID,
|
||||
createCloseIcon: (...args) => contentIcons.createCloseIcon(...args),
|
||||
formatOracleResponseDate: (...args) => opportunitiesFormatters.formatOracleResponseDate(...args),
|
||||
environment
|
||||
});
|
||||
const serviceRequestsAction = createServiceRequestsAction({
|
||||
messageTypes,
|
||||
requestConsumerTokenServiceOnce: (...args) => authContentSession.requestConsumerTokenServiceOnce(...args),
|
||||
sendRuntimeMessage: (...args) => platformContentMessages.sendRuntimeMessage(...args),
|
||||
appendBackgroundRequestLog: (...args) => debugContentLog.appendBackgroundRequestLog(...args),
|
||||
openServiceRequestsModal: (...args) => serviceRequestsView.openServiceRequestsModal(...args),
|
||||
environment
|
||||
});
|
||||
const consumptionPlansAction = createConsumptionPlansAction({
|
||||
messageTypes,
|
||||
appendBackgroundRequestLog: (...args) => debugContentLog.appendBackgroundRequestLog(...args),
|
||||
@@ -443,6 +459,8 @@ function createContentApplication(overrides = {}) {
|
||||
consumptionPlansAction,
|
||||
installBaseAction,
|
||||
installBaseView,
|
||||
serviceRequestsAction,
|
||||
serviceRequestsView,
|
||||
consumptionPlansView,
|
||||
consumptionTrendController,
|
||||
consumptionChart,
|
||||
|
||||
@@ -27,6 +27,7 @@ function createContentModal({
|
||||
authSessionState,
|
||||
closeAccountPlansModal,
|
||||
closeInstallBaseModal,
|
||||
closeServiceRequestsModal,
|
||||
closeCustomerFilterPanel,
|
||||
closeOwnerFilterPanel,
|
||||
closePreferencesModal,
|
||||
@@ -434,6 +435,7 @@ function createContentModal({
|
||||
closePreferencesModal();
|
||||
closeAccountPlansModal();
|
||||
closeInstallBaseModal();
|
||||
closeServiceRequestsModal();
|
||||
modal.hidden = true;
|
||||
}
|
||||
|
||||
@@ -462,6 +464,10 @@ function createContentModal({
|
||||
}
|
||||
|
||||
if (event.key === "Escape") {
|
||||
if (closeServiceRequestsModal()) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (closeInstallBaseModal()) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
|
||||
@@ -11,7 +11,8 @@ function createDebugContentView({
|
||||
const categories = [
|
||||
["all", "All"], ["authentication", "Authentication"],
|
||||
["opportunities", "Opportunities"], ["products", "Products"],
|
||||
["ai", "AI"], ["consumption", "Consumption"], ["installBase", "Installed Base"], ["other", "Other"]
|
||||
["ai", "AI"], ["consumption", "Consumption"], ["installBase", "Installed Base"],
|
||||
["serviceRequests", "Service Requests"], ["other", "Other"]
|
||||
];
|
||||
let selectedCategory = "all";
|
||||
const expandedIds = new Set();
|
||||
@@ -22,6 +23,7 @@ function createDebugContentView({
|
||||
if (entry.name === "requestOpptyProducts") return "products";
|
||||
if (entry.name === "requestGenerateAiSummary") return "ai";
|
||||
if (entry.name === "requestInstallBase") return "installBase";
|
||||
if (entry.name === "requestCustomerServiceRequest") return "serviceRequests";
|
||||
if (/^request(?:AccountSummary|AccountPlans|ConsumptionService)/.test(entry.name)) return "consumption";
|
||||
return "other";
|
||||
}
|
||||
|
||||
@@ -72,11 +72,16 @@
|
||||
}
|
||||
|
||||
.opportunities-extension-cell-actions .opportunities-extension-action-button {
|
||||
flex: 0 0 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.opportunities-extension-table > tbody > tr > td[data-column="customer"] .opportunities-extension-cell-actions {
|
||||
min-width: 84px;
|
||||
}
|
||||
|
||||
.opportunities-extension-cell-actions .opportunities-extension-action-button svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
|
||||
@@ -5,6 +5,7 @@ function createOpportunitiesTable({
|
||||
OPPORTUNITY_DETAIL_URL,
|
||||
createCloudConsumptionButton,
|
||||
createInstallBaseButton,
|
||||
createServiceRequestsButton,
|
||||
createCopyOpportunityButton,
|
||||
createDetailLink,
|
||||
createProductsCountButton,
|
||||
@@ -149,7 +150,7 @@ function createOpportunitiesTable({
|
||||
);
|
||||
const customerActions = document.createElement("span");
|
||||
customerActions.className = "opportunities-extension-cell-actions";
|
||||
customerActions.append(createCloudConsumptionButton(item), createInstallBaseButton(item));
|
||||
customerActions.append(createCloudConsumptionButton(item), createInstallBaseButton(item), createServiceRequestsButton(item));
|
||||
customerCellContent.append(customerLink, customerActions);
|
||||
cell.append(customerCellContent);
|
||||
} else {
|
||||
|
||||
52
src/features/service-requests/action.js
Normal file
52
src/features/service-requests/action.js
Normal file
@@ -0,0 +1,52 @@
|
||||
function createServiceRequestsAction({ messageTypes, requestConsumerTokenServiceOnce, sendRuntimeMessage,
|
||||
appendBackgroundRequestLog, openServiceRequestsModal, environment }) {
|
||||
const { document } = environment;
|
||||
const pending = new Map();
|
||||
|
||||
async function load(partyNumber) {
|
||||
const authorizations = await requestConsumerTokenServiceOnce();
|
||||
const response = await sendRuntimeMessage({
|
||||
type: messageTypes.requestCustomerServiceRequest, partyNumber,
|
||||
authorizations: { accountSummary: authorizations.accountSummary, customerServiceRequests: authorizations.customerServiceRequests }
|
||||
});
|
||||
appendBackgroundRequestLog(response?.debugRequests);
|
||||
if (!response?.ok) throw new Error(response?.error || "Unable to load service requests.");
|
||||
return response.items;
|
||||
}
|
||||
|
||||
function createServiceRequestsButton(opportunity) {
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "opportunities-extension-action-button";
|
||||
button.title = "View customer service requests";
|
||||
button.setAttribute("aria-label", `View service requests for ${opportunity.CustomerAccount?.PartyUniqueName || "customer"}`);
|
||||
const icon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
icon.setAttribute("viewBox", "0 0 24 24");
|
||||
icon.setAttribute("aria-hidden", "true");
|
||||
icon.setAttribute("focusable", "false");
|
||||
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
path.setAttribute("d", "M4 14v-3a8 8 0 0 1 16 0v3 M4 11H3v7h4v-7H4 M20 11h1v7h-4v-7h3 M20 18v1a2 2 0 0 1-2 2h-6");
|
||||
path.setAttribute("fill", "none");
|
||||
path.setAttribute("stroke", "currentColor");
|
||||
path.setAttribute("stroke-width", "1.7");
|
||||
path.setAttribute("stroke-linecap", "round");
|
||||
path.setAttribute("stroke-linejoin", "round");
|
||||
icon.append(path);
|
||||
button.append(icon);
|
||||
button.addEventListener("click", async () => {
|
||||
const view = openServiceRequestsModal(opportunity, button);
|
||||
const partyNumber = String(opportunity.CustomerAccount?.PartyNumber || "");
|
||||
try {
|
||||
if (!partyNumber) throw new Error("Customer Party Number is unavailable.");
|
||||
if (!pending.has(partyNumber)) {
|
||||
pending.set(partyNumber, load(partyNumber).finally(() => pending.delete(partyNumber)));
|
||||
}
|
||||
view.render(await pending.get(partyNumber));
|
||||
} catch (error) {
|
||||
view.render([], error.message || "Unable to load service requests.");
|
||||
}
|
||||
});
|
||||
return button;
|
||||
}
|
||||
return { createServiceRequestsButton };
|
||||
}
|
||||
34
src/features/service-requests/styles.css
Normal file
34
src/features/service-requests/styles.css
Normal file
@@ -0,0 +1,34 @@
|
||||
.opportunities-extension-account-plans-dialog.opportunities-extension-service-requests-dialog { width: min(1700px, 100%); }
|
||||
.opportunities-extension-service-requests-content { min-width: 0; min-height: 0; overflow: auto; scrollbar-gutter: stable; }
|
||||
.opportunities-extension-service-requests-message { margin: 16px; font-size: 13px; }
|
||||
.opportunities-extension-service-requests-table {
|
||||
width: 100%; min-width: 1200px; table-layout: fixed; border-collapse: collapse; font-size: 12px;
|
||||
}
|
||||
.opportunities-extension-service-requests-table col { width: 7%; }
|
||||
.opportunities-extension-service-requests-table .service-requests-col-name { width: 17%; }
|
||||
.opportunities-extension-service-requests-table .service-requests-col-id { width: 10%; }
|
||||
.opportunities-extension-service-requests-table .service-requests-col-severity { width: 5%; }
|
||||
.opportunities-extension-service-requests-table .service-requests-col-subStatus { width: 14%; }
|
||||
.opportunities-extension-service-requests-table .service-requests-col-productName { width: 12%; }
|
||||
.opportunities-extension-service-requests-table th,
|
||||
.opportunities-extension-service-requests-table td {
|
||||
padding: 12px 8px; text-align: left; vertical-align: middle; overflow-wrap: anywhere;
|
||||
border-bottom: 1px solid #e8e5e1; line-height: 1.4;
|
||||
}
|
||||
.opportunities-extension-service-requests-table th { position: sticky; top: 0; z-index: 1; background: #faf9f8; }
|
||||
.opportunities-extension-service-requests-table td[data-field="created"],
|
||||
.opportunities-extension-service-requests-table td[data-field="lastUpdated"],
|
||||
.opportunities-extension-service-requests-table td[data-field="id"] { white-space: nowrap; font-variant-numeric: tabular-nums; }
|
||||
.opportunities-extension-service-request-status {
|
||||
display: inline-block; padding: 3px 6px; border-radius: 12px; font-size: 10px; font-weight: 600;
|
||||
color: #5f5a55; background: #eceae8;
|
||||
}
|
||||
.opportunities-extension-service-request-status[data-status="OPEN"] { color: #17693a; background: #e1f1e5; }
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-service-requests-table th { background: #353331; }
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-service-requests-table th,
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-service-requests-table td { border-color: #484541; }
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-service-request-status { color: #dedbd7; background: #484541; }
|
||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-service-request-status[data-status="OPEN"] { color: #b8e4c6; background: #214d34; }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.opportunities-extension-service-requests-table .opportunities-extension-account-plans-skeleton-bar { animation: none; }
|
||||
}
|
||||
121
src/features/service-requests/view.js
Normal file
121
src/features/service-requests/view.js
Normal file
@@ -0,0 +1,121 @@
|
||||
function createServiceRequestsView({ MODAL_ID, createCloseIcon, formatOracleResponseDate, environment }) {
|
||||
const { document } = environment;
|
||||
let active = null;
|
||||
const columns = [
|
||||
["name", "Name", item => item.customer?.name],
|
||||
["registryId", "Registry ID", item => item.customer?.registryId],
|
||||
["id", "SR Number", item => item.id],
|
||||
["severity", "Severity", item => item.severity],
|
||||
["status", "Status", item => item.status],
|
||||
["subStatus", "Substatus", item => item.subStatus],
|
||||
["productName", "Product Name", item => item.component?.productName],
|
||||
["os", "OS", item => item.os], ["csi", "CSI", item => item.csi],
|
||||
["created", "Creation date", item => item.created],
|
||||
["lastUpdated", "Modified", item => item.lastUpdated]
|
||||
];
|
||||
function element(tag, className, text) {
|
||||
const node = document.createElement(tag);
|
||||
if (className) node.className = className;
|
||||
if (text !== undefined) node.textContent = text;
|
||||
return node;
|
||||
}
|
||||
function closeServiceRequestsModal() {
|
||||
if (!active) return false;
|
||||
const previous = active;
|
||||
active = null;
|
||||
previous.modal.remove();
|
||||
if (previous.trigger?.isConnected) previous.trigger.focus();
|
||||
return true;
|
||||
}
|
||||
function createTable(items, loading = false) {
|
||||
const table = element("table", "opportunities-extension-service-requests-table");
|
||||
table.setAttribute("aria-label", "Customer service requests");
|
||||
const group = element("colgroup");
|
||||
const head = element("thead");
|
||||
const header = element("tr");
|
||||
columns.forEach(([key, label]) => {
|
||||
group.append(element("col", `service-requests-col-${key}`));
|
||||
const th = element("th", "", label);
|
||||
th.scope = "col";
|
||||
header.append(th);
|
||||
});
|
||||
head.append(header);
|
||||
const body = element("tbody");
|
||||
(loading ? Array.from({ length: 8 }, () => ({})) : items).forEach(item => {
|
||||
const row = element("tr");
|
||||
columns.forEach(([key, , getValue]) => {
|
||||
const cell = element("td");
|
||||
cell.dataset.field = key;
|
||||
if (loading) {
|
||||
const bar = element("span", "opportunities-extension-account-plans-skeleton-bar");
|
||||
bar.setAttribute("aria-hidden", "true");
|
||||
cell.append(bar);
|
||||
} else {
|
||||
const value = getValue(item);
|
||||
let text = value === null || value === undefined || value === "" ? "-" : String(value);
|
||||
if (text !== "-" && (key === "created" || key === "lastUpdated")) text = formatOracleResponseDate(value);
|
||||
if (key === "status") {
|
||||
const badge = element("span", "opportunities-extension-service-request-status", text);
|
||||
badge.dataset.status = text.trim().toUpperCase();
|
||||
cell.append(badge);
|
||||
} else cell.textContent = text;
|
||||
}
|
||||
row.append(cell);
|
||||
});
|
||||
body.append(row);
|
||||
});
|
||||
table.append(group, head, body);
|
||||
return table;
|
||||
}
|
||||
function openServiceRequestsModal(opportunity, trigger) {
|
||||
closeServiceRequestsModal();
|
||||
const overlay = document.getElementById(MODAL_ID);
|
||||
if (!overlay || overlay.hidden) return { render() {} };
|
||||
const modal = element("section", "opportunities-extension-account-plans-modal");
|
||||
modal.setAttribute("role", "dialog");
|
||||
modal.setAttribute("aria-modal", "true");
|
||||
modal.setAttribute("aria-busy", "true");
|
||||
modal.setAttribute("aria-labelledby", "opportunities-extension-service-requests-title");
|
||||
const dialog = element("div", "opportunities-extension-account-plans-dialog opportunities-extension-service-requests-dialog");
|
||||
const header = element("header", "opportunities-extension-account-plans-header");
|
||||
const heading = element("div");
|
||||
const title = element("h2", "", "Service Requests");
|
||||
title.id = "opportunities-extension-service-requests-title";
|
||||
heading.append(title, element("p", "", opportunity.CustomerAccount?.PartyUniqueName || "Customer"));
|
||||
const close = element("button", "opportunities-extension-icon-button");
|
||||
close.type = "button";
|
||||
close.title = "Close";
|
||||
close.setAttribute("aria-label", "Close service requests");
|
||||
close.append(createCloseIcon());
|
||||
close.addEventListener("click", closeServiceRequestsModal);
|
||||
header.append(heading, close);
|
||||
const content = element("div", "opportunities-extension-service-requests-content");
|
||||
content.tabIndex = 0;
|
||||
content.setAttribute("role", "region");
|
||||
content.setAttribute("aria-label", "Service requests results");
|
||||
const status = element("p", "opportunities-extension-service-requests-message", "Loading service requests...");
|
||||
status.setAttribute("role", "status");
|
||||
content.append(status, createTable([], true));
|
||||
dialog.append(header, content);
|
||||
modal.append(dialog);
|
||||
overlay.append(modal);
|
||||
active = { modal, trigger };
|
||||
modal.addEventListener("keydown", event => {
|
||||
if (event.key === "Tab") {
|
||||
event.preventDefault();
|
||||
(document.activeElement === close ? content : close).focus();
|
||||
}
|
||||
});
|
||||
close.focus();
|
||||
return { render(items, error = "") {
|
||||
if (active?.modal !== modal || overlay.hidden) return;
|
||||
modal.setAttribute("aria-busy", "false");
|
||||
content.textContent = "";
|
||||
status.textContent = error || (items.length ? `${items.length} service requests` : "No open service requests found for this customer.");
|
||||
status.setAttribute("role", error ? "alert" : "status");
|
||||
content.append(status);
|
||||
if (!error && items.length) content.append(createTable(items));
|
||||
} };
|
||||
}
|
||||
return { openServiceRequestsModal, closeServiceRequestsModal, createTable };
|
||||
}
|
||||
60
src/services/spa/customer-service-requests.js
Normal file
60
src/services/spa/customer-service-requests.js
Normal file
@@ -0,0 +1,60 @@
|
||||
function createSpaCustomerServiceRequests({ ACCOUNT_SUMMARY_URL, fetchJson, findAccountPlanId, normalizeMessageAuthorization }) {
|
||||
const endpoint = "https://spa.oracle.com/oalcrm/web/api/provider-proxy/g2m-custsr-provider/service/oscSr/";
|
||||
const filter = encodeURIComponent(JSON.stringify({ hierarchy: "single", status: "open" }));
|
||||
|
||||
async function requestCustomerServiceRequest(message) {
|
||||
const debugRequests = [];
|
||||
try {
|
||||
const partyNumber = String(message.partyNumber || "").trim();
|
||||
const summaryAuth = normalizeMessageAuthorization(message.authorizations?.accountSummary, "account-ti-authorization");
|
||||
const srAuth = normalizeMessageAuthorization(message.authorizations?.customerServiceRequests, "");
|
||||
if (!partyNumber) throw new Error("Customer Party Number is required.");
|
||||
if (!summaryAuth.value) throw new Error("ACCP-TS authorization is unavailable.");
|
||||
if (!srAuth.value) throw new Error("SPA-ORANGE uiaasHeader.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 accountSummaryId = findAccountPlanId(summary);
|
||||
if ((typeof accountSummaryId !== "string" && typeof accountSummaryId !== "number") || !String(accountSummaryId).trim()) {
|
||||
throw new Error("Account id was not found in requestAccountSummary.");
|
||||
}
|
||||
|
||||
const items = [];
|
||||
let offset = 0;
|
||||
// This endpoint uses totalResults/count rather than hasMore.
|
||||
for (let page = 0; page < 1000; page += 1) {
|
||||
const url = `${endpoint}${encodeURIComponent(String(accountSummaryId))}?customerSRFilter=${filter}&orderBy=creationDate&orderByMode=desc&limit=40&offset=${offset}`;
|
||||
const result = await fetchJson(url, {
|
||||
method: "GET", credentials: "include",
|
||||
// SPA-ORANGE's label may be lead-auth; this provider requires custsr-auth.
|
||||
headers: { Accept: "application/json", "custsr-auth": srAuth.value }
|
||||
}, "requestCustomerServiceRequest", debugRequests);
|
||||
if (!result || !Array.isArray(result.objects)
|
||||
|| result.objects.some(item => !item || typeof item !== "object" || Array.isArray(item))
|
||||
|| !Number.isSafeInteger(result.totalResults) || result.totalResults < 0
|
||||
|| !Number.isSafeInteger(result.count) || result.count !== result.objects.length) {
|
||||
throw new Error("requestCustomerServiceRequest returned an invalid collection.");
|
||||
}
|
||||
if (result.offset !== offset) throw new Error("requestCustomerServiceRequest returned an unexpected offset.");
|
||||
items.push(...result.objects);
|
||||
offset += result.count;
|
||||
if (offset >= result.totalResults) return { ok: true, items, debugRequests };
|
||||
if (!result.count) throw new Error("requestCustomerServiceRequest pagination made no progress.");
|
||||
}
|
||||
throw new Error("requestCustomerServiceRequest pagination limit reached.");
|
||||
} catch (error) {
|
||||
error.debugRequests = debugRequests;
|
||||
throw error;
|
||||
} finally {
|
||||
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 { requestCustomerServiceRequest };
|
||||
}
|
||||
@@ -9,6 +9,7 @@ function createMessageTypes() {
|
||||
requestConsumerTokenService: "opportunitiesExtension.requestConsumerTokenService",
|
||||
requestAccountPlans: "opportunitiesExtension.requestAccountPlans",
|
||||
requestInstallBase: "opportunitiesExtension.requestInstallBase",
|
||||
requestCustomerServiceRequest: "opportunitiesExtension.requestCustomerServiceRequest",
|
||||
requestConsumptionTrend: "opportunitiesExtension.requestConsumptionTrend"
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user