229 lines
13 KiB
JavaScript
229 lines
13 KiB
JavaScript
function createFusionSrDetails({ fetchJson, tabsCreate, tabsRemove, supportsFirefoxCookieStoreTabs, cookiesGetAll, cookieChanges, wait }) {
|
|
const sessions = new Map();
|
|
function cookiePart(cookie) {
|
|
if (!cookie) return null;
|
|
const match = /^ORA_OCIS_CG_SESSION_idcs-([a-zA-Z0-9-]+)_comcipapic-oalprod\.integration\.ocp\.oraclecloud\.com_([12])$/.exec(cookie.name);
|
|
const domain = "comcipapic-oalprod.integration.ocp.oraclecloud.com";
|
|
const cookieDomain = cookie.domain?.replace(/^\./, "");
|
|
if (!match || (cookieDomain && cookieDomain !== domain && !domain.endsWith(`.${cookieDomain}`))) return null;
|
|
return { key: `${match[1]}|${cookie.path || "/"}|${cookie.storeId || ""}`, part: match[2],
|
|
valid: Boolean(cookie.value) && (cookie.expirationDate == null || cookie.expirationDate > Date.now() / 1000) };
|
|
}
|
|
function watchSession(session, sender) {
|
|
const pairs = new Map();
|
|
let available;
|
|
session.cookieReady = new Promise(resolve => { available = resolve; });
|
|
const listener = change => {
|
|
const cookie = change.cookie;
|
|
const part = cookiePart(cookie);
|
|
if (!part) return;
|
|
if (sender?.tab?.cookieStoreId && cookie.storeId !== sender.tab.cookieStoreId) return;
|
|
if (!pairs.has(part.key)) pairs.set(part.key, new Map());
|
|
const pair = pairs.get(part.key);
|
|
if (change.removed || !part.valid) pair.delete(part.part);
|
|
else pair.set(part.part, cookie);
|
|
if (pair.size === 2 && [...pair.values()].every(item => cookiePart(item)?.valid)) available(new Set([part.key]));
|
|
session.wake?.();
|
|
};
|
|
cookieChanges?.addListener(listener);
|
|
session.unwatch = () => cookieChanges?.removeListener(listener);
|
|
}
|
|
function waitForSessionCookies(session, sender) {
|
|
if (session.cookieWait) return session.cookieWait;
|
|
session.cookieWait = (async () => {
|
|
const deadline = Date.now() + 1000;
|
|
for (let attempt = 0; attempt < 5; attempt += 1) {
|
|
// Install the wake-up before reading so a renewal during getAll is not missed.
|
|
const changed = new Promise(resolve => { session.wake = resolve; });
|
|
const current = await sessionCookies(sender);
|
|
const renewed = [...current].some(pair => !session.baseline.has(pair));
|
|
if (renewed || Date.now() >= deadline) {
|
|
session.baseline = current;
|
|
return current;
|
|
}
|
|
await Promise.race([changed, wait(250)]);
|
|
}
|
|
return sessionCookies(sender);
|
|
})().finally(() => { session.cookieWait = null; session.wake = null; });
|
|
return session.cookieWait;
|
|
}
|
|
async function sessionCookies(sender) {
|
|
const domain = "comcipapic-oalprod.integration.ocp.oraclecloud.com";
|
|
const details = { domain };
|
|
if (sender?.tab?.cookieStoreId) details.storeId = sender.tab.cookieStoreId;
|
|
try {
|
|
const groups = new Map();
|
|
for (const cookie of await cookiesGetAll(details)) {
|
|
const part = cookiePart(cookie);
|
|
if (!part?.valid) continue;
|
|
if (!groups.has(part.key)) groups.set(part.key, {});
|
|
groups.get(part.key)[part.part] = [cookie.name, cookie.value, cookie.expirationDate ?? null];
|
|
}
|
|
// Fingerprints remain in memory only; the browser sends the actual Cookie header.
|
|
return new Set([...groups.values()].filter(pair => pair[1] && pair[2]).map(pair => JSON.stringify([pair[1], pair[2]])));
|
|
} catch { return new Set(); }
|
|
}
|
|
async function requestLookupWithSession(operation, message, sender, initialize = false) {
|
|
const existingCookies = initialize ? await sessionCookies(sender) : null;
|
|
if (existingCookies?.size) return { ok: true, debugRequests: [] };
|
|
const logs = [];
|
|
const query = async () => {
|
|
try {
|
|
const result = await operation(message);
|
|
logs.push(...(result.debugRequests || []));
|
|
return { ...result, debugRequests: logs };
|
|
} catch (error) {
|
|
logs.push(...(error.debugRequests || []));
|
|
error.debugRequests = logs;
|
|
throw error;
|
|
}
|
|
};
|
|
if (!initialize) {
|
|
try { return await query(); } catch (error) { if (error.httpStatus !== 401) throw error; }
|
|
}
|
|
const key = sender?.tab?.cookieStoreId || "default";
|
|
let session = sessions.get(key);
|
|
if (!session) {
|
|
const details = {
|
|
url: "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_initiation/live/webApps/external/",
|
|
active: false
|
|
};
|
|
if (Number.isInteger(sender?.tab?.windowId)) details.windowId = sender.tab.windowId;
|
|
if (supportsFirefoxCookieStoreTabs() && sender?.tab?.cookieStoreId) details.cookieStoreId = sender.tab.cookieStoreId;
|
|
session = { users: 0, closed: null, baseline: null };
|
|
watchSession(session, sender);
|
|
session.tab = (async () => {
|
|
session.baseline = existingCookies || await sessionCookies(sender);
|
|
return tabsCreate(details);
|
|
})();
|
|
sessions.set(key, session);
|
|
}
|
|
session.users += 1;
|
|
let tab;
|
|
try {
|
|
tab = await session.tab;
|
|
const deadline = Date.now() + 60000;
|
|
for (let attempt = 0; attempt < 240 && Date.now() < deadline; attempt += 1) {
|
|
const pendingCookies = initialize && attempt === 0
|
|
? sessionCookies(sender) : waitForSessionCookies(session, sender);
|
|
const cookies = await (initialize ? Promise.race([session.cookieReady, pendingCookies]) : pendingCookies);
|
|
if (initialize) {
|
|
// Proactive preparation observes cookies only; API requests belong to the form.
|
|
if (!cookies.size) continue;
|
|
await closeSessionTab();
|
|
return { ok: true, debugRequests: [] };
|
|
}
|
|
try {
|
|
const result = await query();
|
|
// Any successful lookup confirms the shared session; stop the page immediately.
|
|
await closeSessionTab();
|
|
return result;
|
|
} catch (error) {
|
|
if (error.httpStatus !== 401) throw error;
|
|
}
|
|
}
|
|
throw new Error("Service Request session could not be established after 60 seconds. Sign in to Oracle and retry.");
|
|
} catch (error) {
|
|
error.debugRequests = logs;
|
|
throw error;
|
|
} finally {
|
|
session.users -= 1;
|
|
if (session.users === 0) {
|
|
sessions.delete(key);
|
|
session.unwatch();
|
|
await closeSessionTab();
|
|
}
|
|
}
|
|
function closeSessionTab() {
|
|
if (!session.closed && Number.isInteger(tab?.id)) {
|
|
session.closed = tabsRemove(tab.id).catch(() => {});
|
|
}
|
|
return session.closed;
|
|
}
|
|
}
|
|
async function requestInternalServiceRequest(message) {
|
|
const debugRequests = [];
|
|
try {
|
|
const id = String(message.srId ?? "");
|
|
if (!/^\d+$/.test(id)) throw new Error("A numeric SrId is required.");
|
|
const fields = "Title,SrNumber,SrId,OpportunityName_c,QueueManagers_c,StatusCd,resourceMembers;ServiceRequest_SRToInternalSR_Tgt:ReportedHours_c,Title,OpportunityName_c,StatusCd,SrNumber,ServiceNew_c,AccountPartyName,LastUpdateDate,SvcHealth_c,AssigneePersonName,OpenDate,SrId";
|
|
const url = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_SR_management/live;profile=PROD/services/auth/1.1/proxy/oalsetCRMRestAPI/uri/https/eeho.fa.us2.oraclecloud.com/crmRestApi/resources/latest/serviceRequests"
|
|
+ `?fields=${encodeURIComponent(fields)}&onlyData=true&q=${encodeURIComponent(`SrId=${id}`)}`;
|
|
const query = async () => {
|
|
const result = await fetchJson(url, { method: "GET", credentials: "include",
|
|
headers: { Accept: "application/json", Authorization: "Session" }
|
|
}, "requestInternalServiceRequest", debugRequests);
|
|
if (!Array.isArray(result?.items)) throw new Error("Invalid SR details response.");
|
|
return result;
|
|
};
|
|
const result = await query();
|
|
const items = result.items.flatMap(item => {
|
|
const children = item.ServiceRequest_SRToInternalSR_Tgt;
|
|
if (children == null) return [];
|
|
if (!Array.isArray(children) || children.some(child => !child || typeof child !== "object")) throw new Error("Invalid linked SR collection.");
|
|
return children;
|
|
});
|
|
return { ok: true, items, debugRequests };
|
|
} catch (error) {
|
|
error.debugRequests = debugRequests;
|
|
throw error;
|
|
}
|
|
}
|
|
async function requestLookup(lookupType, requestName) {
|
|
const debugRequests = [];
|
|
try {
|
|
const url = `https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_initiation/live;profile=PROD/services/auth/1.1/proxy/oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/crm/lookups?lookupType=${lookupType}&type=STANDARD`;
|
|
const result = await fetchJson(url, { method: "GET", credentials: "include",
|
|
headers: { Accept: "application/json", Authorization: "Session" }
|
|
}, requestName, debugRequests);
|
|
if (!Array.isArray(result?.items) || result.items.some(item => typeof item?.code !== "string" || typeof item?.name !== "string")) throw new Error(`Invalid ${requestName} response.`);
|
|
const items = result.items.filter(item => item.enabledFlag !== "N")
|
|
.sort((a, b) => (Number(a.displaySequence) || 0) - (Number(b.displaySequence) || 0))
|
|
.map(({ code, name }) => ({ code, name }));
|
|
return { ok: true, items, debugRequests };
|
|
} catch (error) { error.debugRequests = debugRequests; throw error; }
|
|
}
|
|
async function requestSrLookupOpportunityBusiness(message) {
|
|
const debugRequests = [];
|
|
try {
|
|
if (typeof message.emailAddress !== "string" || !message.emailAddress.trim()) throw new Error("Current user email is unavailable.");
|
|
const url = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_initiation/live;profile=PROD/services/auth/1.1/proxy/oke_seaas/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/crm/services"
|
|
+ `?emailAddress=${encodeURIComponent(message.emailAddress.trim())}&salesObject=OPPORTUNITY&serviceType=BUSINESS`;
|
|
const result = await fetchJson(url, { method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" } }, "requestSrLookupOpportunityBusiness", debugRequests);
|
|
if (!Array.isArray(result?.items) || result.items.some(item => !/^\d+$/.test(String(item?.Id ?? "")) || typeof item?.RecordName !== "string")) throw new Error("Invalid Services response.");
|
|
return { ok: true, items: result.items.map(({ Id, RecordName, Abstract_c }) => ({ Id: String(Id), RecordName, Abstract_c: Abstract_c == null ? "" : String(Abstract_c) })), debugRequests };
|
|
} catch (error) { error.debugRequests = debugRequests; throw error; }
|
|
}
|
|
async function requestInternalSrOptions(message, services = false) {
|
|
const debugRequests = [];
|
|
const name = services ? "requestInternalSrServices" : "requestLookupTypePlatform";
|
|
try {
|
|
const base = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_SR_management/live;profile=PROD/services/auth/1.1/proxy/oalsetSeaaSOKECustomRestAPI/uri/https/gxpap.oracle.com/oalcrm/service/set/seaas/crm/";
|
|
let path = "lookups?type=STANDARD&lookupType=SENGG_TECH_PLATFORM";
|
|
if (services) {
|
|
const id = String(message.parentServiceId ?? "");
|
|
if (!/^\d+$/.test(id)) throw new Error("A numeric parent SrId is required.");
|
|
if (typeof message.emailAddress !== "string" || !message.emailAddress.trim()) throw new Error("Current user email is unavailable.");
|
|
path = `services?emailAddress=${encodeURIComponent(message.emailAddress.trim())}&parentServiceId=${encodeURIComponent(id)}&serviceType=INTERNAL`;
|
|
}
|
|
const result = await fetchJson(base + path, { method: "GET", credentials: "include",
|
|
headers: { Accept: "application/json", Authorization: "Session" }
|
|
}, name, debugRequests);
|
|
if (!Array.isArray(result?.items) || result.items.some(item => services
|
|
? !/^\d+$/.test(String(item?.Id ?? "")) || typeof item?.RecordName !== "string"
|
|
: typeof item?.code !== "string" || typeof item?.name !== "string")) throw new Error(`Invalid ${name} response.`);
|
|
const items = services
|
|
? result.items.map(item => ({ Id: String(item.Id), RecordName: item.RecordName, Description_c: String(item.Description_c ?? "") }))
|
|
: result.items.filter(item => item.enabledFlag !== "N").map(({ code, name }) => ({ code, name }));
|
|
return { ok: true, items, debugRequests };
|
|
} catch (error) { error.debugRequests = debugRequests; throw error; }
|
|
}
|
|
return { requestLookupWithSession, requestInternalServiceRequest, requestSrLookupOpportunityBusiness,
|
|
requestLookupTypePlatform: () => requestInternalSrOptions({}),
|
|
requestInternalSrServices: message => requestInternalSrOptions(message, true),
|
|
prepareSession: sender => requestLookupWithSession(null, {}, sender, true),
|
|
requestSrLookupPrimaryPillar: () => requestLookup("SENGG_PRIMARY_PILLAR", "requestSrLookupPrimaryPillar"),
|
|
requestSrLookupLanguage: () => requestLookup("SENGG_LANGUAGE", "requestSrLookupLanguage"),
|
|
requestSrLookupTechPlatform: () => requestLookup("SENGG_TECH_PLATFORM", "requestSrLookupTechPlatform") };
|
|
}
|