|
|
|
|
@@ -1,4 +1,4 @@
|
|
|
|
|
function createFusionSrCreate({ fetchJson, tokenHeaders, previewTokenHeaders }) {
|
|
|
|
|
function createFusionSrCreate({ fetchJson, tokenHeaders, previewTokenHeaders, internalServiceRequestHeaders }) {
|
|
|
|
|
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";
|
|
|
|
|
@@ -11,26 +11,76 @@ function createFusionSrCreate({ fetchJson, tokenHeaders, previewTokenHeaders })
|
|
|
|
|
if (typeof value !== "string" || !value.trim()) throw new Error(`Missing ${name}.`);
|
|
|
|
|
return value.trim();
|
|
|
|
|
}
|
|
|
|
|
function dateOnly(value, name) {
|
|
|
|
|
const date = text(value, name);
|
|
|
|
|
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 ${name}.`);
|
|
|
|
|
return date;
|
|
|
|
|
}
|
|
|
|
|
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.");
|
|
|
|
|
dateOnly(payload.CustomerDeadline_c, "Customer deadline");
|
|
|
|
|
return { ...payload, SvcQuestionsCollection_c: [], SourceCd: "SVC_OPPORTUNITY_SCREEN", SeverityCd: "ORA_SVC_SEV2", ChannelTypeCd: "ORA_SVC_WEB" };
|
|
|
|
|
}
|
|
|
|
|
async function requestResourceUser(email, debugRequests) {
|
|
|
|
|
const emailAddress = text(email, "current user email");
|
|
|
|
|
const resource = await fetchJson(`${resourceUsers}?emailAddress=${encodeURIComponent(emailAddress)}`, {
|
|
|
|
|
method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" }
|
|
|
|
|
}, "requestResourceUser", debugRequests);
|
|
|
|
|
return numericId(resource?.resourcePartyId, "resourcePartyId");
|
|
|
|
|
}
|
|
|
|
|
async function requestPostInternalServiceRequest(message) {
|
|
|
|
|
const debugRequests = [];
|
|
|
|
|
try {
|
|
|
|
|
const source = message.payload;
|
|
|
|
|
const parentSrId = numericId(source?.ServiceRequest_Id_SRToInternalSR, "parent SrId");
|
|
|
|
|
const payload = {
|
|
|
|
|
Title: text(source?.Title, "Title"),
|
|
|
|
|
ServiceNew_Id_c: numericId(source?.ServiceNew_Id_c, "ServiceNew_Id_c"),
|
|
|
|
|
ProblemDescription: text(source?.ProblemDescription, "Description"),
|
|
|
|
|
CustomerDeadline_c: dateOnly(source?.CustomerDeadline_c, "Customer deadline"),
|
|
|
|
|
TechPlatformLookUp_c: text(source?.TechPlatformLookUp_c, "Platform"),
|
|
|
|
|
InternalDeadline_c: `${dateOnly(source?.InternalDeadline_c, "Internal Deadline")}T00:00:00.000Z`,
|
|
|
|
|
SvcQuestionsCollection_c: []
|
|
|
|
|
};
|
|
|
|
|
const parent = await requestSRInfo(parentSrId, debugRequests);
|
|
|
|
|
payload.ServiceRequest_Id_SRToInternalSR = String(parentSrId);
|
|
|
|
|
for (const name of ["AccountPartyId", "OpportunityName_Id_c"]) payload[name] = numericId(parent[name], name);
|
|
|
|
|
for (const name of ["OpportunityName_c", "RegionText_c", "SvcHealth_c", "Language_c", "CountryText_c", "PrimaryPillarNew_c"]) payload[name] = text(parent[name], name);
|
|
|
|
|
payload.SecondaryPillarNew_c = parent.SecondaryPillarNew_c == null ? null : text(parent.SecondaryPillarNew_c, "SecondaryPillarNew_c");
|
|
|
|
|
payload.ReportedByPartyId = await requestResourceUser(message.emailAddress, debugRequests);
|
|
|
|
|
const url = "https://comcipapic-oalprod.integration.ocp.oraclecloud.com/ic/builder/rt/SeaaS_SR_management/live;profile=PROD/services/auth/1.1/proxy/oalsetCRMRestAPICloudAccount/uri/https/eeho.fa.us2.oraclecloud.com/crmRestApi/resources/latest/serviceRequests";
|
|
|
|
|
const result = await fetchJson(url, { method: "POST", credentials: "include",
|
|
|
|
|
headers: await internalServiceRequestHeaders(url),
|
|
|
|
|
body: JSON.stringify(payload)
|
|
|
|
|
}, "requestPostInternalServiceRequest", debugRequests, { allowEmpty: true });
|
|
|
|
|
return { ok: true, result, debugRequests };
|
|
|
|
|
} catch (error) { error.debugRequests = debugRequests; throw error; }
|
|
|
|
|
}
|
|
|
|
|
async function requestSRInfo(parentSrId, debugRequests) {
|
|
|
|
|
const url = new 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");
|
|
|
|
|
url.searchParams.set("fields", "PrimaryPillarNew_c,SecondaryPillarNew_c,SvcHealth_c,SrNumber,AccountPartyId,CountryText_c,Language_c,OpportunityName_Id_c,OpportunityName_c,RegionText_c,ServiceNew_Id_c");
|
|
|
|
|
url.searchParams.set("onlyData", "true");
|
|
|
|
|
url.searchParams.set("orderBy", "LastUpdateDate:desc");
|
|
|
|
|
url.searchParams.set("q", `SrId=${parentSrId}`);
|
|
|
|
|
const result = await fetchJson(url.href, {
|
|
|
|
|
method: "GET", credentials: "include", headers: { Accept: "application/json", Authorization: "Session" }
|
|
|
|
|
}, "requestSRInfo", debugRequests);
|
|
|
|
|
if (!Array.isArray(result?.items) || result.items.length !== 1 || !result.items[0]
|
|
|
|
|
|| (result.items[0].SrId != null && String(result.items[0].SrId) !== String(parentSrId))) {
|
|
|
|
|
throw new Error("requestSRInfo did not return the selected parent Service Request.");
|
|
|
|
|
}
|
|
|
|
|
return result.items[0];
|
|
|
|
|
}
|
|
|
|
|
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");
|
|
|
|
|
payload.ReportedByPartyId = await requestResourceUser(message.emailAddress, debugRequests);
|
|
|
|
|
const sharedHeaders = await previewTokenHeaders(catalog);
|
|
|
|
|
return { ok: true, payload, debugRequests, headers: {
|
|
|
|
|
requestSrTokenCatalog: sharedHeaders,
|
|
|
|
|
@@ -38,34 +88,9 @@ function createFusionSrCreate({ fetchJson, tokenHeaders, previewTokenHeaders })
|
|
|
|
|
} };
|
|
|
|
|
}
|
|
|
|
|
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 headers = await requestCreationHeaders(message.catalogId, endpoint, debugRequests);
|
|
|
|
|
const result = await fetchJson(endpoint, {
|
|
|
|
|
method: "POST", credentials: "include", headers: { ...catalogHeaders, "vb-proxy-header-x-oracle-ectoken": token.result.trim() },
|
|
|
|
|
method: "POST", credentials: "include", headers,
|
|
|
|
|
body: JSON.stringify(payload)
|
|
|
|
|
}, "requestPostServiceRequest", debugRequests, { allowEmpty: true });
|
|
|
|
|
return { ok: true, result, debugRequests };
|
|
|
|
|
@@ -73,16 +98,43 @@ function createFusionSrCreate({ fetchJson, tokenHeaders, previewTokenHeaders })
|
|
|
|
|
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)";
|
|
|
|
|
annotateSigningRequest(debugRequests);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
async function requestCreationHeaders(id, target, debugRequests) {
|
|
|
|
|
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, target);
|
|
|
|
|
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.");
|
|
|
|
|
}
|
|
|
|
|
return { ...catalogHeaders, "vb-proxy-header-x-oracle-ectoken": token.result.trim() };
|
|
|
|
|
}
|
|
|
|
|
function annotateSigningRequest(debugRequests) {
|
|
|
|
|
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";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return { requestPostServiceRequest };
|
|
|
|
|
return { requestPostServiceRequest, requestPostInternalServiceRequest };
|
|
|
|
|
}
|
|
|
|
|
|