Table of Contents

UCI Troubleshooting Tests

Console Tests

The following tests can be executed in the developer tools console to inspect and troubleshoot the UCI Cache Buster

List out the current versions in the cache

(async () => {
  const solution = "SenseiControlsV1";
  const controls = ["IQ.AltusAssist", "IQ.AltusDashboard", "IQ.GridCellRenderer"];
  const targets = ["https://uci-state/", "WebResources"];

  // Match the app's frame resolution: look up at most two levels for Dynamics.
  const dynamicsWindow = (() => {
    try {
      let candidate = window;
      if (candidate.__preload == null) candidate = candidate.parent;
      if (candidate.__preload == null) candidate = candidate.parent;
      return candidate.__preload == null ? window : candidate;
    } catch {
      return window;
    }
  })();

  const cacheStorage = dynamicsWindow.caches;
  const existing = cacheStorage ? await cacheStorage.keys() : [];
  const deployed = dynamicsWindow.SenseiIqTelemetryProperties?.SolutionVersions
    ?.find(v => String(v.solutionName).toLowerCase() === solution.toLowerCase())?.version;

  let sentinel;
  try {
    sentinel = JSON.parse(dynamicsWindow.sessionStorage.getItem("altus.uciCacheBust") || "null");
  } catch (e) {
    sentinel = `unreadable: ${e.name}`;
  }

  let serviceWorkerCount;
  try {
    serviceWorkerCount = (
      await dynamicsWindow.navigator?.serviceWorker?.getRegistrations?.() || []
    ).length;
  } catch (e) {
    serviceWorkerCount = `unavailable: ${e.name}`;
  }

  const cacheEntryCounts = {};
  for (const name of targets) {
    cacheEntryCounts[name] = !cacheStorage
      ? "unavailable"
      : existing.includes(name)
        ? (await (await cacheStorage.open(name)).keys()).length
        : "missing";
  }

  const rows = [];
  const findControl = name =>
    controls.find(control =>
      String(name || "").toLowerCase().includes(control.toLowerCase())
    );

  const add = (control, kind, name, cached) => {
    if (typeof cached === "string" && cached) {
      rows.push({
        control,
        kind,
        name,
        cached,
        deployed: deployed || "missing",
        stale: deployed ? cached !== deployed : "unknown"
      });
    }
  };

  if (cacheStorage && existing.includes(targets[0])) {
    const cache = await cacheStorage.open(targets[0]);
    const [request] = await cache.keys();
    const response = request ? await cache.match(request) : null;
    const state = response ? await response.json() : {};

    for (const resource of Object.values(state?.metadata?.webResources || {})) {
      const control = findControl(resource?.name);
      if (
        control &&
        String(resource?.solutionName || "").toLowerCase() === solution.toLowerCase()
      ) {
        add(control, "resource", resource.name, resource.solutionVersion);
      }
    }

    for (const manifest of Object.values(state?.metadata?.controlManifests || {})) {
      const control = findControl(manifest?.ConstructorName);
      if (
        control &&
        String(manifest?.SolutionName || "").toLowerCase() === solution.toLowerCase()
      ) {
        add(control, "manifest", manifest.ConstructorName, manifest.SolutionVersion);
      }
    }
  }

  for (const control of controls) {
    if (!rows.some(row => row.control === control)) {
      rows.push({
        control,
        kind: "not cached",
        name: "No matching entry in uci-state",
        cached: "missing",
        deployed: deployed || "missing",
        stale: "unknown"
      });
    }
  }

  console.log({
    at: new Date().toISOString(),
    inspectedWindowIsCurrent: dynamicsWindow === window,
    cacheStorageAvailable: !!cacheStorage,
    serviceWorkerCount,
    cacheEntryCounts,
    deployedVersion: deployed || "missing",
    sentinel
  });
  console.table(rows);
})().catch(console.error);

Stale cache example:

Stale UCI cache

Fresh cache example:

Fresh UCI Cache

Create a UCI Cache Issue


(async () => {
  const CONTROL = "IQ.AltusDashboard";
  const SOLUTION = "SenseiControlsV1";
  const CORRUPTED_VERSION = "0.0.0.0";
  const SENTINEL = "altus.uciCacheBust";
  const cache = await caches.open("https://uci-state/");
  const requests = await cache.keys();

  if (requests.length === 0) throw new Error("No uci-state entry found.");

  const request = requests[0];
  const response = await cache.match(request);

  if (!response) throw new Error("Unable to read uci-state entry.");

  const state = JSON.parse(await response.text());

  let changed = 0;

  const isTargetSolution = (value) =>
    typeof value === "string" && value.toLowerCase() === SOLUTION.toLowerCase();

  for (const resource of Object.values(state.metadata?.webResources ?? {})) {
    if (
      resource?.name?.startsWith(`cc_${CONTROL}/`) &&
      isTargetSolution(resource.solutionName)
    ) {
      resource.solutionVersion = CORRUPTED_VERSION;
      changed++;
    }
  }

  for (const manifest of Object.values(
    state.metadata?.controlManifests ?? {},
  )) {
    if (
      manifest?.ConstructorName === CONTROL &&
      isTargetSolution(manifest.SolutionName)
    ) {
      manifest.SolutionVersion = CORRUPTED_VERSION;
      changed++;
    }
  }

  if (changed === 0) {
    throw new Error(`No cached entries found for ${CONTROL}.`);
  }

  await cache.put(
    request,
    new Response(JSON.stringify(state), {
      headers: { "Content-Type": "application/json" },
    }),
  );

  sessionStorage.removeItem(SENTINEL);

  console.table({
    control: CONTROL,
    corruptedEntries: changed,
    corruptedVersion: CORRUPTED_VERSION,
    stateEntry: request.url,
  });
  location.reload();
})().catch(console.error);

Cleanup after testing


(async () => {

    await caches.delete("https://uci-state/");

    await caches.delete("WebResources");

    sessionStorage.removeItem("altus.uciCacheBust");

    location.reload();

})();