(function () { "use strict"; const ORACLE_DOMAIN = "eeho.fa.us2.oraclecloud.com"; const ORACLE_COOKIE_URLS = [ "https://eeho.fa.us2.oraclecloud.com/", "https://eeho.fa.us2.oraclecloud.com/hcmUI/faces/FuseWelcome", "https://eeho.fa.us2.oraclecloud.com/fscmUI/faces/FuseWelcome", "https://eeho.fa.us2.oraclecloud.com/fscmRestApi/tokenrelay" ]; const XSRF_COOKIE_NAME = "XSRF-TOKEN-US2DZ2V_F"; const XSRF_COOKIE_PREFIX = "XSRF-TOKEN-"; const OPPORTUNITIES_LIST_URL = "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/cx-sales/application/container/opportunities/opportunities-list"; const COOKIE_REFRESH_TIMEOUT_MS = 15000; const COOKIE_REFRESH_POLL_MS = 250; const OPPTY_PRODUCTS_CACHE_KEY = "opportunitiesExtension.opptyProductsCache.v1"; const OPPTY_PRODUCTS_PAGE_LIMIT = 15; const OPPTY_PRODUCTS_MAX_PAGES = 250; const OPPTY_PRODUCTS_CONCURRENCY = 4; const OPPTY_PRODUCTS_FIELDS = [ "ProductType", "Description", "InventoryItemId", "ProdGroupName", "ProdGroupId", "OwnerLockAsgnFlag", "Quantity", "RecurTypeCode", "RevnAmountCurcyCode", "UnitPrice", "RevnAmount", "PriceTypeCode", "EffectiveDate", "Name1", "NonRecurringRevenue", "OpportunityOwnerPartyName", "OpportunityOwnerResourcePartyId", "OptyId", "OptyNumber", "PartyName2", "PrTerritoryVersionId", "PrTerritoryVersionIdForManual", "RecurEndDate", "RecurFrequencyCode", "RecurNumberPeriods", "RecurRevenue", "ResourcePartyId", "RevnId", "RevnNumber", "SplitPercent", "SalesCreditTypeCode", "SplitTypeCode", "TerrOwnerPartyName", "UpsideAmount", "UsageRevenue", "StatusCode", "BUOrgId", "TypeCode", "WinProb", "ForecastType_c", "ARRLocalCurrency_c", "CPQLastSyncDate_c", "CPQOperationType_c", "ProdGLID_c", "ProposalNumber_c", "QuoteNumber_c", "ServicesPeriod_c", "CPQIntLastUpdatedBy_c", "CPQUpdatedAmount_c", "CPQUpdatedQuantity_c", "CPQUpdatedRevnType_c", "CPQUpdatedServicePeriod_c", "CPQUpdatedStatus_c", "OrderNumber_c", "PrevAmount_c", "PrevQuantity_c", "PrevRevenueType_c", "PrevServicePeriod_c", "PrevStatus_c", "SubscriptionID_c", "WorkloadName_c", "ConsumptionStartDate_c", "RampMonths_c" ]; const runtimeApi = typeof browser !== "undefined" ? browser : chrome; let productsCachePromise = null; let productsCacheWritePromise = Promise.resolve(); const inFlightProducts = new Map(); runtimeApi.runtime.onMessage.addListener((message, sender, sendResponse) => { if (!message || !message.type) { return false; } if (message.type === "opportunitiesExtension.getXsrfToken") { getXsrfTokenCookie() .then((result) => { sendResponse({ ok: true, cookieName: result.cookie ? result.cookie.name : "", token: result.cookie ? result.cookie.value : "", matchedCookieNames: result.matchedCookieNames, lookupDetails: result.lookupDetails }); }) .catch((error) => { sendResponse({ ok: false, cookieName: "", token: "", matchedCookieNames: [], lookupDetails: [], error: error.message || "Unable to read cookies." }); }); return true; } if (message.type === "opportunitiesExtension.refreshXsrfCookie") { refreshXsrfCookie(sender) .then(sendResponse) .catch((error) => { sendResponse({ ok: false, refreshed: false, cookieName: "", durationMs: 0, error: error.message || "Unable to refresh XSRF cookie." }); }); return true; } if (message.type === "opportunitiesExtension.requestOpptyProducts") { requestOpptyProducts(message, sender) .then(sendResponse) .catch((error) => { sendResponse({ ok: false, requestId: message.requestId || "", error: error.message || "Unable to load opportunity products." }); }); return true; } return false; }); async function requestOpptyProducts(message, sender) { const requestId = typeof message.requestId === "string" ? message.requestId : ""; const accessToken = typeof message.accessToken === "string" ? message.accessToken : ""; const cacheMaxAgeMs = normalizeCacheMaxAge(message.cacheMaxAgeMs); const optyNumbers = Array.from(new Set( (Array.isArray(message.optyNumbers) ? message.optyNumbers : []) .filter(Boolean) .map(String) )); if (!requestId || !accessToken) { throw new Error("A request id and access token are required for requestOpptyProducts."); } let nextIndex = 0; let completed = 0; let failed = 0; let cacheHits = 0; const runWorker = async () => { while (nextIndex < optyNumbers.length) { const index = nextIndex; nextIndex += 1; const optyNumber = optyNumbers[index]; try { const result = await getOpptyProducts(optyNumber, accessToken, cacheMaxAgeMs); completed += 1; if (result.fromCache) { cacheHits += 1; } await sendProductsProgress(sender, { type: "opportunitiesExtension.opptyProductsProgress", requestId, state: "running", optyNumber, items: result.items, fromCache: result.fromCache, cachedAt: result.cachedAt, completed, total: optyNumbers.length, failed, cacheHits }); } catch (error) { completed += 1; failed += 1; await sendProductsProgress(sender, { type: "opportunitiesExtension.opptyProductsProgress", requestId, state: "running", optyNumber, items: [], error: error.message || "Unable to load products.", completed, total: optyNumbers.length, failed, cacheHits }); } } }; const workerCount = Math.min(OPPTY_PRODUCTS_CONCURRENCY, Math.max(optyNumbers.length, 1)); await Promise.all(Array.from({ length: workerCount }, runWorker)); const state = failed > 0 ? "complete-with-errors" : "complete"; await sendProductsProgress(sender, { type: "opportunitiesExtension.opptyProductsProgress", requestId, state, completed, total: optyNumbers.length, failed, cacheHits }); return { ok: failed === 0, requestId, completed, total: optyNumbers.length, failed, cacheHits }; } async function getOpptyProducts(optyNumber, accessToken, cacheMaxAgeMs) { const cache = await getProductsCache(); const cachedEntry = cache[optyNumber]; if (cachedEntry && Array.isArray(cachedEntry.items) && Date.now() - Number(cachedEntry.fetchedAt) < cacheMaxAgeMs) { return { items: cachedEntry.items, fromCache: true, cachedAt: Number(cachedEntry.fetchedAt) }; } if (!inFlightProducts.has(optyNumber)) { const request = fetchAllOpptyProducts(optyNumber, accessToken) .then(async (items) => { const fetchedAt = Date.now(); cache[optyNumber] = { fetchedAt, items }; await saveProductsCache(cache).catch(() => {}); return { items, fetchedAt }; }) .finally(() => { inFlightProducts.delete(optyNumber); }); inFlightProducts.set(optyNumber, request); } const fetchedResult = await inFlightProducts.get(optyNumber); return { items: fetchedResult.items, cachedAt: fetchedResult.fetchedAt, fromCache: false }; } async function fetchAllOpptyProducts(optyNumber, accessToken) { let offset = 0; let page = 0; const items = []; while (page < OPPTY_PRODUCTS_MAX_PAGES) { const response = await fetch(createOpptyProductsUrl(optyNumber, offset), { method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}` } }); if (!response.ok) { throw new Error(`requestOpptyProducts failed with status ${response.status}.`); } const responseData = await response.json(); const pageItems = Array.isArray(responseData.items) ? responseData.items : []; items.push(...pageItems); page += 1; if (!responseData.hasMore) { return items; } const responseOffset = Number(responseData.offset); const responseCount = Number(responseData.count); const receivedCount = Number.isFinite(responseCount) ? responseCount : pageItems.length; const currentOffset = Number.isFinite(responseOffset) ? responseOffset : offset; if (receivedCount <= 0) { throw new Error("requestOpptyProducts returned hasMore without additional results."); } offset = currentOffset + receivedCount; } throw new Error("requestOpptyProducts exceeded the pagination safety limit."); } function createOpptyProductsUrl(optyNumber, offset) { const url = new URL(`https://${ORACLE_DOMAIN}/crmRestApi/rest/rv:be91c002-2e5d-4ed2-a37e-e0c837bf141f/en/11.13.18.05:9/opportunities/${encodeURIComponent(optyNumber)}/child/ChildRevenue`); url.searchParams.set("onlyData", "true"); url.searchParams.set("q", "(SplitTypeCode!='DETAILCHILDSPLIT') AND (RecurTypeCode!='CHILDRECUR')"); url.searchParams.set("totalResults", "false"); url.searchParams.set("fields", OPPTY_PRODUCTS_FIELDS.join(",")); url.searchParams.set("orderBy", "CreationDate:desc"); url.searchParams.set("limit", String(OPPTY_PRODUCTS_PAGE_LIMIT)); url.searchParams.set("offset", String(offset)); return url.toString(); } function normalizeCacheMaxAge(value) { const maxAge = Number(value); const defaultMaxAge = 24 * 60 * 60 * 1000; const maximumMaxAge = 30 * 24 * 60 * 60 * 1000; return Number.isFinite(maxAge) && maxAge > 0 ? Math.min(maxAge, maximumMaxAge) : defaultMaxAge; } async function getProductsCache() { if (!productsCachePromise) { productsCachePromise = storageLocalGet(OPPTY_PRODUCTS_CACHE_KEY) .then((result) => result && typeof result[OPPTY_PRODUCTS_CACHE_KEY] === "object" ? result[OPPTY_PRODUCTS_CACHE_KEY] : {}) .catch(() => ({})); } return productsCachePromise; } function saveProductsCache(cache) { productsCacheWritePromise = productsCacheWritePromise .catch(() => {}) .then(() => storageLocalSet({ [OPPTY_PRODUCTS_CACHE_KEY]: cache })); return productsCacheWritePromise; } function sendProductsProgress(sender, message) { if (!sender || !sender.tab || !Number.isInteger(sender.tab.id)) { return Promise.resolve(); } const options = Number.isInteger(sender.frameId) ? { frameId: sender.frameId } : undefined; return tabsSendMessage(sender.tab.id, message, options).catch(() => {}); } async function refreshXsrfCookie(sender) { const startedAt = Date.now(); const previous = (await getXsrfTokenCookie()).cookie; const createProperties = { url: OPPORTUNITIES_LIST_URL, active: false }; if (sender && sender.tab && Number.isInteger(sender.tab.windowId)) { createProperties.windowId = sender.tab.windowId; } if (supportsFirefoxCookieStoreTabs() && sender && sender.tab && sender.tab.cookieStoreId) { createProperties.cookieStoreId = sender.tab.cookieStoreId; } let refreshTab; try { refreshTab = await tabsCreate(createProperties); const current = await waitForXsrfCookieChange(previous); const refreshed = hasCookieChanged(previous, current); return { ok: Boolean(current), refreshed, cookieName: current ? current.name : "", durationMs: Date.now() - startedAt, error: refreshed ? "" : "XSRF cookie was not created or changed before timeout." }; } finally { if (refreshTab && Number.isInteger(refreshTab.id)) { try { await tabsRemove(refreshTab.id); } catch (error) { // The temporary tab may already have been closed. } } } } async function waitForXsrfCookieChange(previousCookie) { const deadline = Date.now() + COOKIE_REFRESH_TIMEOUT_MS; let currentCookie = null; while (Date.now() < deadline) { currentCookie = (await getXsrfTokenCookie()).cookie; if (hasCookieChanged(previousCookie, currentCookie)) { return currentCookie; } await wait(COOKIE_REFRESH_POLL_MS); } return currentCookie; } function hasCookieChanged(previousCookie, currentCookie) { if (!currentCookie || !currentCookie.value) { return false; } if (!previousCookie) { return true; } return previousCookie.name !== currentCookie.name || previousCookie.value !== currentCookie.value || previousCookie.expirationDate !== currentCookie.expirationDate; } function supportsFirefoxCookieStoreTabs() { return Boolean( runtimeApi.runtime && typeof runtimeApi.runtime.getBrowserInfo === "function" ); } function tabsCreate(details) { if (runtimeApi.tabs.create.length <= 1) { return runtimeApi.tabs.create(details); } return new Promise((resolve, reject) => { runtimeApi.tabs.create(details, (tab) => { const lastError = runtimeApi.runtime.lastError; if (lastError) { reject(new Error(lastError.message)); return; } resolve(tab); }); }); } function tabsRemove(tabId) { if (runtimeApi.tabs.remove.length <= 1) { return runtimeApi.tabs.remove(tabId); } return new Promise((resolve, reject) => { runtimeApi.tabs.remove(tabId, () => { const lastError = runtimeApi.runtime.lastError; if (lastError) { reject(new Error(lastError.message)); return; } resolve(); }); }); } function tabsSendMessage(tabId, message, options) { if (typeof browser !== "undefined") { return options ? runtimeApi.tabs.sendMessage(tabId, message, options) : runtimeApi.tabs.sendMessage(tabId, message); } return new Promise((resolve, reject) => { const callback = (response) => { const lastError = runtimeApi.runtime.lastError; if (lastError) { reject(new Error(lastError.message)); return; } resolve(response); }; if (options) { runtimeApi.tabs.sendMessage(tabId, message, options, callback); } else { runtimeApi.tabs.sendMessage(tabId, message, callback); } }); } function storageLocalGet(key) { if (typeof browser !== "undefined") { return runtimeApi.storage.local.get(key); } return new Promise((resolve, reject) => { runtimeApi.storage.local.get(key, (result) => { const lastError = runtimeApi.runtime.lastError; if (lastError) { reject(new Error(lastError.message)); return; } resolve(result); }); }); } function storageLocalSet(value) { if (typeof browser !== "undefined") { return runtimeApi.storage.local.set(value); } return new Promise((resolve, reject) => { runtimeApi.storage.local.set(value, () => { const lastError = runtimeApi.runtime.lastError; if (lastError) { reject(new Error(lastError.message)); return; } resolve(); }); }); } function wait(durationMs) { return new Promise((resolve) => setTimeout(resolve, durationMs)); } async function getXsrfTokenCookie() { const lookupDetails = []; const allCookies = []; const stores = await getCookieStores(lookupDetails); for (const store of stores) { const exactCookie = await getExactCookieFromUrls(store.id, lookupDetails); if (exactCookie) { allCookies.push(exactCookie); } } for (const store of stores) { await collectCookies({ name: XSRF_COOKIE_NAME, storeId: store.id }, allCookies, lookupDetails); } for (const store of stores) { await collectCookies({ domain: ORACLE_DOMAIN, storeId: store.id }, allCookies, lookupDetails); await collectCookies({ domain: `.${ORACLE_DOMAIN}`, storeId: store.id }, allCookies, lookupDetails); } for (const store of stores) { for (const url of ORACLE_COOKIE_URLS) { await collectCookies({ url, storeId: store.id }, allCookies, lookupDetails); } } const uniqueCookies = dedupeCookies(allCookies); const xsrfCookies = uniqueCookies.filter((cookie) => cookie.name.startsWith(XSRF_COOKIE_PREFIX)); const exactCookie = xsrfCookies.find((cookie) => cookie.name === XSRF_COOKIE_NAME); const hostCookie = xsrfCookies.find((cookie) => cookie.domain === ORACLE_DOMAIN || cookie.domain === `.${ORACLE_DOMAIN}`); return { cookie: exactCookie || hostCookie || xsrfCookies[0] || null, matchedCookieNames: xsrfCookies.map((cookie) => `${cookie.name} (${cookie.domain}${cookie.path})`), lookupDetails }; } async function getExactCookieFromUrls(storeId, lookupDetails) { for (const url of ORACLE_COOKIE_URLS) { const cookie = await cookiesGet({ url, name: XSRF_COOKIE_NAME, storeId }); lookupDetails.push(`${JSON.stringify({ url, name: XSRF_COOKIE_NAME, storeId })} => ${cookie ? "found" : "not found"}`); if (cookie) { return cookie; } } return null; } async function getCookieStores(lookupDetails) { try { const stores = await cookiesGetAllCookieStores(); lookupDetails.push(`getAllCookieStores => ${stores.length} store(s)`); return stores.length ? stores : [{ id: undefined }]; } catch (error) { lookupDetails.push(`getAllCookieStores => unavailable (${error.message || "unknown error"})`); return [{ id: undefined }]; } } async function collectCookies(details, target, lookupDetails) { const cleanDetails = removeUndefinedValues(details); const cookies = await cookiesGetAll(cleanDetails); target.push(...cookies); lookupDetails.push(`${JSON.stringify(cleanDetails)} => ${cookies.length} cookie(s)`); } function dedupeCookies(cookies) { const seen = new Set(); return cookies.filter((cookie) => { const key = `${cookie.name}|${cookie.domain}|${cookie.path}|${cookie.storeId || ""}`; if (seen.has(key)) { return false; } seen.add(key); return true; }); } function cookiesGetAll(details) { if (runtimeApi.cookies.getAll.length <= 1) { return runtimeApi.cookies.getAll(details); } return new Promise((resolve, reject) => { runtimeApi.cookies.getAll(details, (cookies) => { const lastError = runtimeApi.runtime.lastError; if (lastError) { reject(new Error(lastError.message)); return; } resolve(cookies); }); }); } function cookiesGet(details) { const cleanDetails = removeUndefinedValues(details); if (runtimeApi.cookies.get.length <= 1) { return runtimeApi.cookies.get(cleanDetails); } return new Promise((resolve, reject) => { runtimeApi.cookies.get(cleanDetails, (cookie) => { const lastError = runtimeApi.runtime.lastError; if (lastError) { reject(new Error(lastError.message)); return; } resolve(cookie); }); }); } function cookiesGetAllCookieStores() { if (!runtimeApi.cookies.getAllCookieStores) { return Promise.resolve([{ id: undefined }]); } if (runtimeApi.cookies.getAllCookieStores.length === 0) { return runtimeApi.cookies.getAllCookieStores(); } return new Promise((resolve, reject) => { runtimeApi.cookies.getAllCookieStores((stores) => { const lastError = runtimeApi.runtime.lastError; if (lastError) { reject(new Error(lastError.message)); return; } resolve(stores); }); }); } function removeUndefinedValues(details) { return Object.fromEntries( Object.entries(details).filter((entry) => entry[1] !== undefined) ); } })();