Adjustment to the request debug view functionality
This commit is contained in:
101
dist/firefox/background.js
vendored
101
dist/firefox/background.js
vendored
@@ -8,6 +8,7 @@ function createMessageTypes() {
|
||||
refreshXsrfCookie: "opportunitiesExtension.refreshXsrfCookie",
|
||||
requestOpptyProducts: "opportunitiesExtension.requestOpptyProducts",
|
||||
opptyProductsProgress: "opportunitiesExtension.opptyProductsProgress",
|
||||
opptyProductsRequestDebug: "opportunitiesExtension.opptyProductsRequestDebug",
|
||||
requestConsumerTokenService: "opportunitiesExtension.requestConsumerTokenService",
|
||||
requestAccountPlans: "opportunitiesExtension.requestAccountPlans",
|
||||
requestConsumptionTrend: "opportunitiesExtension.requestConsumptionTrend"
|
||||
@@ -757,6 +758,15 @@ function createProductsBackgroundJobs({
|
||||
let completed = 0;
|
||||
let failed = 0;
|
||||
let cacheHits = 0;
|
||||
let debugDelivery = Promise.resolve();
|
||||
const reportRequest = entry => {
|
||||
const message = {
|
||||
type: messageTypes.opptyProductsRequestDebug,
|
||||
requestId,
|
||||
entry: { ...entry, debugKey: JSON.stringify([requestId, entry.metadata.optyNumber, entry.metadata.offset]) }
|
||||
};
|
||||
debugDelivery = debugDelivery.catch(() => {}).then(() => sendProductsProgress(sender, message));
|
||||
};
|
||||
|
||||
const runWorker = async () => {
|
||||
while (nextIndex < optyNumbers.length) {
|
||||
@@ -765,7 +775,7 @@ function createProductsBackgroundJobs({
|
||||
const optyNumber = optyNumbers[index];
|
||||
|
||||
try {
|
||||
const result = await getOpptyProducts(optyNumber, accessToken, cacheMaxAgeMs);
|
||||
const result = await getOpptyProducts(optyNumber, accessToken, cacheMaxAgeMs, reportRequest);
|
||||
completed += 1;
|
||||
|
||||
if (result.fromCache) {
|
||||
@@ -806,6 +816,7 @@ function createProductsBackgroundJobs({
|
||||
|
||||
const workerCount = Math.min(OPPTY_PRODUCTS_CONCURRENCY, Math.max(optyNumbers.length, 1));
|
||||
await Promise.all(Array.from({ length: workerCount }, runWorker));
|
||||
await debugDelivery.catch(() => {});
|
||||
|
||||
const state = failed > 0 ? "complete-with-errors" : "complete";
|
||||
await sendProductsProgress(sender, {
|
||||
@@ -848,7 +859,7 @@ function createProductsCache({
|
||||
storageLocalGet,
|
||||
storageLocalSet
|
||||
}) {
|
||||
async function getOpptyProducts(optyNumber, accessToken, cacheMaxAgeMs) {
|
||||
async function getOpptyProducts(optyNumber, accessToken, cacheMaxAgeMs, onDebug = () => {}) {
|
||||
const cache = await getProductsCache();
|
||||
const cachedEntry = cache[optyNumber];
|
||||
|
||||
@@ -857,7 +868,14 @@ function createProductsCache({
|
||||
}
|
||||
|
||||
if (!productsCacheState.inFlightProducts.has(optyNumber)) {
|
||||
const request = fetchAllOpptyProducts(optyNumber, accessToken)
|
||||
const flight = { listeners: new Set(), entries: new Map(), promise: null };
|
||||
productsCacheState.inFlightProducts.set(optyNumber, flight);
|
||||
flight.promise = fetchAllOpptyProducts(optyNumber, accessToken, entry => {
|
||||
flight.entries.set(entry.metadata.offset, entry);
|
||||
for (const listener of flight.listeners) {
|
||||
try { listener(entry); } catch (_) {}
|
||||
}
|
||||
})
|
||||
.then(async (items) => {
|
||||
const fetchedAt = Date.now();
|
||||
cache[optyNumber] = {
|
||||
@@ -870,15 +888,20 @@ function createProductsCache({
|
||||
.finally(() => {
|
||||
productsCacheState.inFlightProducts.delete(optyNumber);
|
||||
});
|
||||
productsCacheState.inFlightProducts.set(optyNumber, request);
|
||||
}
|
||||
|
||||
const fetchedResult = await productsCacheState.inFlightProducts.get(optyNumber);
|
||||
return {
|
||||
items: fetchedResult.items,
|
||||
cachedAt: fetchedResult.fetchedAt,
|
||||
fromCache: false
|
||||
};
|
||||
const flight = productsCacheState.inFlightProducts.get(optyNumber);
|
||||
// A second tab/job shares the fetch but still receives its current diagnostics.
|
||||
flight.listeners.add(onDebug);
|
||||
for (const entry of flight.entries.values()) {
|
||||
try { onDebug(entry); } catch (_) {}
|
||||
}
|
||||
try {
|
||||
const fetchedResult = await flight.promise;
|
||||
return { items: fetchedResult.items, cachedAt: fetchedResult.fetchedAt, fromCache: false };
|
||||
} finally {
|
||||
flight.listeners.delete(onDebug);
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeCacheMaxAge(value) {
|
||||
@@ -918,29 +941,67 @@ function createFusionProducts({
|
||||
OPPTY_PRODUCTS_MAX_PAGES,
|
||||
OPPTY_PRODUCTS_PAGE_LIMIT,
|
||||
ORACLE_DOMAIN,
|
||||
createBackgroundDebugEntry,
|
||||
completeBackgroundDebugEntry,
|
||||
createDebugBody,
|
||||
headersToObject,
|
||||
environment
|
||||
}) {
|
||||
const { fetch } = environment;
|
||||
async function fetchAllOpptyProducts(optyNumber, accessToken) {
|
||||
async function fetchAllOpptyProducts(optyNumber, accessToken, onDebug = () => {}) {
|
||||
let offset = 0;
|
||||
let page = 0;
|
||||
const items = [];
|
||||
|
||||
while (page < OPPTY_PRODUCTS_MAX_PAGES) {
|
||||
const response = await fetch(createOpptyProductsUrl(optyNumber, offset), {
|
||||
const url = createOpptyProductsUrl(optyNumber, offset);
|
||||
const options = {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${accessToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`requestOpptyProducts failed with status ${response.status}.`);
|
||||
};
|
||||
const entry = createBackgroundDebugEntry("requestOpptyProducts", url, {
|
||||
...options, headers: { Accept: "application/json", Authorization: "(present)" }
|
||||
}, new Date());
|
||||
entry.metadata = { executionContext: "background", optyNumber, offset, page: page + 1 };
|
||||
const report = () => {
|
||||
// Diagnostics must not block requests or transmit the Bearer credential.
|
||||
try { onDebug({ ...entry, metadata: { ...entry.metadata } }); } catch (_) {}
|
||||
};
|
||||
let response;
|
||||
let responseBody = { value: "", truncated: false };
|
||||
let responseData;
|
||||
report();
|
||||
try {
|
||||
response = await fetch(url, options);
|
||||
entry.httpStatus = response.status;
|
||||
entry.statusText = response.statusText || "";
|
||||
entry.responseHeaders = headersToObject(response.headers);
|
||||
Object.keys(entry.responseHeaders).forEach(key => {
|
||||
if (/authorization|cookie|token/i.test(key)) entry.responseHeaders[key] = "(present)";
|
||||
});
|
||||
entry.metadata.phase = "reading-response";
|
||||
report();
|
||||
const text = await response.text();
|
||||
responseBody = createDebugBody(text);
|
||||
if (!response.ok) throw new Error(`requestOpptyProducts failed with status ${response.status}.`);
|
||||
responseData = JSON.parse(text);
|
||||
if (!responseData || typeof responseData !== "object") throw new Error("requestOpptyProducts returned an invalid response.");
|
||||
completeBackgroundDebugEntry(entry, response, responseBody, "");
|
||||
} catch (error) {
|
||||
completeBackgroundDebugEntry(entry, response, responseBody, error.message || "Unable to load products.");
|
||||
entry.status = "failed";
|
||||
throw error;
|
||||
} finally {
|
||||
Object.keys(entry.responseHeaders).forEach(key => {
|
||||
if (/authorization|cookie|token/i.test(key)) entry.responseHeaders[key] = "(present)";
|
||||
});
|
||||
entry.metadata.phase = "complete";
|
||||
report();
|
||||
}
|
||||
|
||||
const responseData = await response.json();
|
||||
const pageItems = Array.isArray(responseData.items) ? responseData.items : [];
|
||||
items.push(...pageItems);
|
||||
page += 1;
|
||||
@@ -1561,6 +1622,10 @@ function createBackgroundApplication(overrides = {}) {
|
||||
storageLocalSet: (...args) => platformBackgroundApi.storageLocalSet(...args)
|
||||
});
|
||||
const fusionProducts = createFusionProducts({
|
||||
createBackgroundDebugEntry: (...args) => httpBackgroundJson.createBackgroundDebugEntry(...args),
|
||||
completeBackgroundDebugEntry: (...args) => httpBackgroundJson.completeBackgroundDebugEntry(...args),
|
||||
createDebugBody: (...args) => httpBackgroundJson.createDebugBody(...args),
|
||||
headersToObject: (...args) => httpBackgroundJson.headersToObject(...args),
|
||||
OPPTY_PRODUCTS_FIELDS: contracts.OPPTY_PRODUCTS_FIELDS,
|
||||
OPPTY_PRODUCTS_MAX_PAGES: contracts.OPPTY_PRODUCTS_MAX_PAGES,
|
||||
OPPTY_PRODUCTS_PAGE_LIMIT: contracts.OPPTY_PRODUCTS_PAGE_LIMIT,
|
||||
|
||||
119
dist/firefox/content.js
vendored
119
dist/firefox/content.js
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user