Implemented the functionality to create Service Requests
This commit is contained in:
193
dist/chromium/background.js
vendored
193
dist/chromium/background.js
vendored
@@ -19,6 +19,7 @@ function createMessageTypes() {
|
||||
requestMgmtAction: "opportunitiesExtension.requestMgmtAction",
|
||||
requestInternalServiceRequest: "opportunitiesExtension.requestInternalServiceRequest",
|
||||
requestSrLookupPrimaryPillar: "opportunitiesExtension.requestSrLookupPrimaryPillar",
|
||||
requestPostServiceRequest: "opportunitiesExtension.requestPostServiceRequest",
|
||||
prepareSrSession: "opportunitiesExtension.prepareSrSession",
|
||||
requestSrLookupLanguage: "opportunitiesExtension.requestSrLookupLanguage",
|
||||
requestSrLookupTechPlatform: "opportunitiesExtension.requestSrLookupTechPlatform",
|
||||
@@ -430,6 +431,90 @@ function createAuthSpaCredentials({
|
||||
return { normalizeMessageAuthorization, extractTokenServiceAuthorization };
|
||||
}
|
||||
|
||||
// Source: src/auth/sr-request-headers.js
|
||||
function createSrRequestHeaders({ runtimeApi }) {
|
||||
const origin = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com";
|
||||
const appPath = "/ic/builder/rt/SeaaS_initiation/";
|
||||
const storageKey = "opportunitiesExtension.srAppBuilderClientId.v1";
|
||||
const maxAge = 60 * 60 * 1000;
|
||||
let observed = null;
|
||||
let started = false;
|
||||
function valid(record) {
|
||||
return record?.origin === origin && typeof record.value === "string"
|
||||
&& /^[\x21-\x7e]{1,2048}$/.test(record.value)
|
||||
&& Number.isFinite(record.observedAt) && record.observedAt <= Date.now()
|
||||
&& Date.now() - record.observedAt < maxAge;
|
||||
}
|
||||
function observeHeaders(rawUrl, headers) {
|
||||
let url;
|
||||
try { url = new URL(rawUrl); } catch { return; }
|
||||
if (url.origin !== origin || !/^\/ic\/builder\/rt\/SeaaS_initiation\/live(?:\/|;)/.test(url.pathname)) return;
|
||||
// Read only the client ID, not cookies, bodies or authorization.
|
||||
const value = typeof headers?.get === "function" ? headers.get("x-appbuilder-client-id")
|
||||
: headers?.find(item => item.name.toLowerCase() === "x-appbuilder-client-id")?.value;
|
||||
const record = { origin, value: value?.trim(), observedAt: Date.now() };
|
||||
if (!valid(record)) return;
|
||||
observed = record;
|
||||
// Session storage survives service-worker suspension without persisting across browser sessions.
|
||||
runtimeApi.storage?.session?.set({ [storageKey]: record }).catch(() => {});
|
||||
}
|
||||
function start() {
|
||||
if (started || !runtimeApi?.webRequest) return;
|
||||
started = true;
|
||||
const filter = { urls: [`${origin}${appPath}*`], types: ["xmlhttprequest"] };
|
||||
runtimeApi.webRequest.onBeforeSendHeaders?.addListener(details => {
|
||||
if (details.tabId >= 0 && !details.incognito) observeHeaders(details.url, details.requestHeaders);
|
||||
}, filter, ["requestHeaders"]);
|
||||
runtimeApi.webRequest.onHeadersReceived?.addListener(details => {
|
||||
if (!details.incognito) observeHeaders(details.url, details.responseHeaders);
|
||||
}, filter, ["responseHeaders"]);
|
||||
}
|
||||
async function previewTokenHeaders(url) {
|
||||
const target = new URL(url);
|
||||
if (target.origin !== origin || !target.pathname.startsWith(appPath) || !/\/Catalogue_c(?:\/\d+)?$/.test(target.pathname)) {
|
||||
throw new Error("Unexpected Service Request token URL.");
|
||||
}
|
||||
if (!valid(observed)) {
|
||||
try {
|
||||
const stored = await runtimeApi.storage?.session?.get(storageKey);
|
||||
if (valid(stored?.[storageKey]) && !valid(observed)) observed = stored[storageKey];
|
||||
} catch { /* A missing capture must not prevent trying the other required headers. */ }
|
||||
}
|
||||
return {
|
||||
Accept: "application/json", "Content-Type": "application/vnd.oracle.adf.action+json",
|
||||
Authorization: "Session", Origin: target.origin,
|
||||
...(valid(observed) ? { "x-appbuilder-client-id": observed.value } : {})
|
||||
};
|
||||
}
|
||||
async function tokenHeaders(url, serviceRequestUrl) {
|
||||
const headers = await previewTokenHeaders(url);
|
||||
if (!/\/Catalogue_c\/\d+$/.test(new URL(url).pathname)) throw new Error("Missing Catalogue Id.");
|
||||
const serviceTarget = new URL(serviceRequestUrl);
|
||||
if (serviceTarget.origin !== headers.Origin || !serviceTarget.pathname.startsWith(appPath)
|
||||
|| !serviceTarget.pathname.endsWith("/set/seaas/mgmt/serviceRequests") || serviceTarget.search || serviceTarget.hash) {
|
||||
throw new Error("Unexpected Service Request URL.");
|
||||
}
|
||||
if (!runtimeApi?.declarativeNetRequest?.updateSessionRules || !runtimeApi.runtime?.getURL) {
|
||||
throw new Error("Reload the extension to enable the Service Request token header permissions.");
|
||||
}
|
||||
const extensionDomain = new URL(runtimeApi.runtime.getURL("/")).hostname;
|
||||
const catalogUrl = url.slice(0, url.lastIndexOf("/") + 1);
|
||||
const escapedCatalogUrl = catalogUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
const escapedServiceUrl = serviceTarget.href.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
await runtimeApi.declarativeNetRequest.updateSessionRules({
|
||||
removeRuleIds: [7201, 7202],
|
||||
addRules: [`^${escapedCatalogUrl}[0-9]+$`, `^${escapedServiceUrl}$`].map((regexFilter, index) => ({
|
||||
id: 7201 + index, priority: 1,
|
||||
action: { type: "modifyHeaders", requestHeaders: [{ header: "Origin", operation: "set", value: headers.Origin }] },
|
||||
condition: { regexFilter, initiatorDomains: [extensionDomain],
|
||||
requestMethods: ["post"], resourceTypes: ["xmlhttprequest"] }
|
||||
}))
|
||||
});
|
||||
return headers;
|
||||
}
|
||||
return { start, tokenHeaders, previewTokenHeaders, observeResponseHeaders: observeHeaders };
|
||||
}
|
||||
|
||||
// Source: src/services/spa/accounts.js
|
||||
function createSpaAccounts({
|
||||
ACCOUNT_PLANS_PAYLOAD,
|
||||
@@ -951,6 +1036,96 @@ function createFusionSrDetails({ fetchJson, tabsCreate, tabsRemove, supportsFire
|
||||
requestSrLookupTechPlatform: () => requestLookup("SENGG_TECH_PLATFORM", "requestSrLookupTechPlatform") };
|
||||
}
|
||||
|
||||
// Source: src/services/fusion/sr-create.js
|
||||
function createFusionSrCreate({ fetchJson, tokenHeaders, previewTokenHeaders }) {
|
||||
const base = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_initiation/live;profile=PROD/services/auth/1.1/proxy/";
|
||||
const catalog = base + "crmRestApiResources/uri/https/eeho.fa.us2.oraclecloud.com/crmRestApi/resources/11.13.18.05/Catalogue_c";
|
||||
const resourceUsers = base + "oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/crm/resourceUsers";
|
||||
const endpoint = base + "oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/mgmt/serviceRequests";
|
||||
function numericId(value, name) {
|
||||
if (!/^\d+$/.test(String(value ?? "")) || !Number.isSafeInteger(Number(value)) || Number(value) <= 0) throw new Error(`Missing or invalid ${name}.`);
|
||||
return Number(value);
|
||||
}
|
||||
function text(value, name) {
|
||||
if (typeof value !== "string" || !value.trim()) throw new Error(`Missing ${name}.`);
|
||||
return value.trim();
|
||||
}
|
||||
function payloadFor(input) {
|
||||
const payload = {};
|
||||
for (const name of ["AccountPartyId", "OpportunityName_Id_c", "ServiceNew_Id_c"]) payload[name] = numericId(input?.[name], name);
|
||||
for (const name of ["Title", "RegionText_c", "ProblemDescription", "Language_c", "CustomerDeadline_c", "TechPlatformLookUp_c", "CountryText_c", "PrimaryPillarNew_c"]) payload[name] = text(input?.[name], name);
|
||||
const date = payload.CustomerDeadline_c;
|
||||
if (!/^\d{4}-\d{2}-\d{2}$/.test(date) || !Number.isFinite(Date.parse(`${date}T00:00:00Z`))
|
||||
|| new Date(`${date}T00:00:00Z`).toISOString().slice(0, 10) !== date) throw new Error("Invalid Customer deadline.");
|
||||
return { ...payload, SvcQuestionsCollection_c: [], SourceCd: "SVC_OPPORTUNITY_SCREEN", SeverityCd: "ORA_SVC_SEV2", ChannelTypeCd: "ORA_SVC_WEB" };
|
||||
}
|
||||
async function requestPostServiceRequest(message) {
|
||||
const debugRequests = [];
|
||||
try {
|
||||
if (message.phase !== "prepare" && message.phase !== "confirm") throw new Error("Service Request confirmation is required.");
|
||||
const payload = payloadFor(message.payload);
|
||||
if (message.phase === "prepare") {
|
||||
const emailAddress = text(message.emailAddress, "current user email");
|
||||
const resource = await fetchJson(`${resourceUsers}?emailAddress=${encodeURIComponent(emailAddress)}`, {
|
||||
method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" }
|
||||
}, "requestResourceUser", debugRequests);
|
||||
payload.ReportedByPartyId = numericId(resource?.resourcePartyId, "resourcePartyId");
|
||||
const sharedHeaders = await previewTokenHeaders(catalog);
|
||||
return { ok: true, payload, debugRequests, headers: {
|
||||
requestSrTokenCatalog: sharedHeaders,
|
||||
requestPostServiceRequest: { ...sharedHeaders, "vb-proxy-header-x-oracle-ectoken": "(generated after confirmation)" }
|
||||
} };
|
||||
}
|
||||
payload.ReportedByPartyId = numericId(message.payload?.ReportedByPartyId, "ReportedByPartyId");
|
||||
let id = message.catalogId;
|
||||
if (id == null) {
|
||||
const result = await fetchJson(`${catalog}?onlyData=true&fields=Id&limit=1`, {
|
||||
method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" }
|
||||
}, "requestSrCatalog", debugRequests);
|
||||
id = result?.items?.[0]?.Id;
|
||||
}
|
||||
id = numericId(id, "Catalogue Id");
|
||||
const tokenUrl = `${catalog}/${id}`;
|
||||
let catalogHeaders = await tokenHeaders(tokenUrl, endpoint);
|
||||
const requestToken = () => fetchJson(tokenUrl, {
|
||||
method: "POST", credentials: "include", headers: catalogHeaders, body: JSON.stringify({ name: "getJwtToken" })
|
||||
}, "requestSrTokenCatalog", debugRequests);
|
||||
let token;
|
||||
try {
|
||||
token = await requestToken();
|
||||
} catch (error) {
|
||||
if (error.httpStatus !== 403) throw error;
|
||||
const refreshed = await previewTokenHeaders(tokenUrl);
|
||||
if (!refreshed["x-appbuilder-client-id"] || refreshed["x-appbuilder-client-id"] === catalogHeaders["x-appbuilder-client-id"]) throw error;
|
||||
catalogHeaders = refreshed;
|
||||
token = await requestToken();
|
||||
}
|
||||
if (debugRequests.at(-1)?.httpStatus !== 200 || typeof token?.result !== "string" || !token.result.trim()) {
|
||||
throw new Error("requestSrTokenCatalog did not return HTTP 200 with a token.");
|
||||
}
|
||||
const result = await fetchJson(endpoint, {
|
||||
method: "POST", credentials: "include", headers: { ...catalogHeaders, "vb-proxy-header-x-oracle-ectoken": token.result.trim() },
|
||||
body: JSON.stringify(payload)
|
||||
}, "requestPostServiceRequest", debugRequests, { allowEmpty: true });
|
||||
return { ok: true, result, debugRequests };
|
||||
} catch (error) {
|
||||
error.debugRequests = debugRequests;
|
||||
throw error;
|
||||
} finally {
|
||||
// Never forward the signing token to content scripts, even with Debug secrets enabled.
|
||||
for (const entry of debugRequests) {
|
||||
if (entry.name === "requestSrTokenCatalog") {
|
||||
entry.metadata.appBuilderClientId = entry.requestHeaders?.["x-appbuilder-client-id"] ? "Observed in SeaaS requests" : "Not observed in SeaaS requests";
|
||||
entry.responseBody = JSON.stringify({ result: "(redacted)" });
|
||||
entry.responseBodyTruncated = false;
|
||||
}
|
||||
if (entry.requestHeaders?.["vb-proxy-header-x-oracle-ectoken"]) entry.requestHeaders["vb-proxy-header-x-oracle-ectoken"] = "(redacted)";
|
||||
}
|
||||
}
|
||||
}
|
||||
return { requestPostServiceRequest };
|
||||
}
|
||||
|
||||
// Source: src/services/spa/current-user.js
|
||||
function createSpaCurrentUser({ fetchJson }) {
|
||||
async function requestCurrentUser() {
|
||||
@@ -1156,6 +1331,7 @@ function createSpaResponseShapes() {
|
||||
function createHttpBackgroundJson({
|
||||
previewDebugBody,
|
||||
DEBUG_BODY_LIMIT,
|
||||
observeResponseHeaders = () => {},
|
||||
environment
|
||||
}) {
|
||||
const { fetch } = environment;
|
||||
@@ -1172,6 +1348,7 @@ function createHttpBackgroundJson({
|
||||
throw error;
|
||||
}
|
||||
|
||||
observeResponseHeaders(response.url || url, response.headers);
|
||||
const responsePayload = await readResponsePayload(response);
|
||||
const responseBody = createDebugBody(responsePayload.text);
|
||||
completeBackgroundDebugEntry(debugEntry, response, responseBody, "");
|
||||
@@ -2049,6 +2226,7 @@ function registerBackgroundMessages({
|
||||
requestActionTeamMembers,
|
||||
requestActionFindLookup,
|
||||
requestMgmtAction,
|
||||
requestPostServiceRequest,
|
||||
requestInternalServiceRequest,
|
||||
requestSrLookupPrimaryPillar,
|
||||
requestSrLookupWithSession,
|
||||
@@ -2068,6 +2246,11 @@ runtimeApi.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (!message || !message.type) {
|
||||
return false;
|
||||
}
|
||||
if (message.type === messageTypes.requestPostServiceRequest) {
|
||||
requestPostServiceRequest(message).then(sendResponse).catch(error => sendResponse({ ok: false,
|
||||
error: error.message, debugRequests: error.debugRequests || [] }));
|
||||
return true;
|
||||
}
|
||||
if (message.type === messageTypes.prepareSrSession) {
|
||||
prepareSrSession(sender).then(sendResponse).catch(error => sendResponse({ ok: false,
|
||||
error: error.message, debugRequests: error.debugRequests || [] }));
|
||||
@@ -2306,6 +2489,7 @@ function createBackgroundApplication(overrides = {}) {
|
||||
});
|
||||
const spaResponseShapes = createSpaResponseShapes();
|
||||
const httpBackgroundJson = createHttpBackgroundJson({
|
||||
observeResponseHeaders: (...args) => srRequestHeaders.observeResponseHeaders(...args),
|
||||
previewDebugBody: createDebugBodyPreview,
|
||||
DEBUG_BODY_LIMIT: contracts.DEBUG_BODY_LIMIT,
|
||||
environment
|
||||
@@ -2375,6 +2559,12 @@ function createBackgroundApplication(overrides = {}) {
|
||||
const requestRatecardByPlan = (...args) => spaConsumption.requestRatecardByPlan(...args);
|
||||
const requestOpptyProducts = (...args) => productsBackgroundJobs.requestOpptyProducts(...args);
|
||||
const runtimeApi = environment.browser || environment.chrome;
|
||||
const srRequestHeaders = createSrRequestHeaders({ runtimeApi });
|
||||
const fusionSrCreate = createFusionSrCreate({
|
||||
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args),
|
||||
tokenHeaders: (...args) => srRequestHeaders.tokenHeaders(...args),
|
||||
previewTokenHeaders: url => srRequestHeaders.previewTokenHeaders(url)
|
||||
});
|
||||
const fusionSrDetails = createFusionSrDetails({
|
||||
cookieChanges: runtimeApi?.cookies?.onChanged,
|
||||
cookiesGetAll: (...args) => platformBackgroundApi.cookiesGetAll(...args),
|
||||
@@ -2385,7 +2575,8 @@ function createBackgroundApplication(overrides = {}) {
|
||||
fetchJson: (...args) => httpBackgroundJson.fetchJson(...args)
|
||||
});
|
||||
function start() {
|
||||
startBackgroundApplication({ prepareSrSession: sender => fusionSrDetails.prepareSession(sender), requestSrLookupWithSession: (...args) => fusionSrDetails.requestLookupWithSession(...args), messageTypes, requestSrLookupLanguage: () => fusionSrDetails.requestSrLookupLanguage(), requestSrLookupTechPlatform: () => fusionSrDetails.requestSrLookupTechPlatform(), requestSrLookupOpportunityBusiness: message => fusionSrDetails.requestSrLookupOpportunityBusiness(message), requestSrLookupPrimaryPillar: () => fusionSrDetails.requestSrLookupPrimaryPillar(), requestInternalServiceRequest: (message, sender) => fusionSrDetails.requestInternalServiceRequest(message, sender), requestMgmtAction: message => spaActionOptions.requestMgmtAction(message), invalidateProductsCache: () => productsCache.invalidate(), requestCurrentUser, getXsrfTokenCookie, refreshXsrfCookie, requestAccountPlans, requestInstallBase, requestCustomerServiceRequest, requestWlActions, requestActionTeamMembers, requestActionFindLookup, requestConsumerTokenService, requestConsumptionTrend, requestConsumptionPlanDetail, requestRatecardByPlan, requestOpptyProducts, runtimeApi });
|
||||
srRequestHeaders.start();
|
||||
startBackgroundApplication({ requestPostServiceRequest: message => fusionSrCreate.requestPostServiceRequest(message), prepareSrSession: sender => fusionSrDetails.prepareSession(sender), requestSrLookupWithSession: (...args) => fusionSrDetails.requestLookupWithSession(...args), messageTypes, requestSrLookupLanguage: () => fusionSrDetails.requestSrLookupLanguage(), requestSrLookupTechPlatform: () => fusionSrDetails.requestSrLookupTechPlatform(), requestSrLookupOpportunityBusiness: message => fusionSrDetails.requestSrLookupOpportunityBusiness(message), requestSrLookupPrimaryPillar: () => fusionSrDetails.requestSrLookupPrimaryPillar(), requestInternalServiceRequest: (message, sender) => fusionSrDetails.requestInternalServiceRequest(message, sender), requestMgmtAction: message => spaActionOptions.requestMgmtAction(message), invalidateProductsCache: () => productsCache.invalidate(), requestCurrentUser, getXsrfTokenCookie, refreshXsrfCookie, requestAccountPlans, requestInstallBase, requestCustomerServiceRequest, requestWlActions, requestActionTeamMembers, requestActionFindLookup, requestConsumerTokenService, requestConsumptionTrend, requestConsumptionPlanDetail, requestRatecardByPlan, requestOpptyProducts, runtimeApi });
|
||||
}
|
||||
return {
|
||||
start, contracts,
|
||||
|
||||
Reference in New Issue
Block a user