This is an automated email from the ASF dual-hosted git repository.

JiaLiangC pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 1de1669b52 AMBARI-26659: Installation wizards persist not working and 
change tab icon to ambari (#4221)
1de1669b52 is described below

commit 1de1669b5265ce32206119ba6cc1be8b1a8ccdf1
Author: Sandeep  Kumar <[email protected]>
AuthorDate: Wed Sep 16 09:16:09 2026 +0530

    AMBARI-26659: Installation wizards persist not working and change tab icon 
to ambari (#4221)
---
 ambari-web/latest/index.html                       |   4 ++-
 ambari-web/latest/public/ambari-logo.png           | Bin 0 -> 2779 bytes
 ambari-web/latest/public/vite.svg                  |   1 -
 .../latest/src/Utils/persistedSettings.test.ts     |  37 ++++++++++++++++++++-
 ambari-web/latest/src/Utils/persistedSettings.ts   |  30 +++++++++++++++++
 ambari-web/latest/src/api/clusterApi.test.ts       |  21 ++++++++++++
 ambari-web/latest/src/api/clusterApi.ts            |   3 +-
 7 files changed, 92 insertions(+), 4 deletions(-)

diff --git a/ambari-web/latest/index.html b/ambari-web/latest/index.html
index 292a977e49..c786d74ad7 100755
--- a/ambari-web/latest/index.html
+++ b/ambari-web/latest/index.html
@@ -20,7 +20,9 @@
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
-    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <!-- Relative so the icon still resolves when the app is served from a
+         sub-path rather than the web root (vite base is "./"). -->
+    <link rel="icon" type="image/png" href="./ambari-logo.png" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>Ambari</title>
   </head>
diff --git a/ambari-web/latest/public/ambari-logo.png 
b/ambari-web/latest/public/ambari-logo.png
new file mode 100644
index 0000000000..07d31ee480
Binary files /dev/null and b/ambari-web/latest/public/ambari-logo.png differ
diff --git a/ambari-web/latest/public/vite.svg 
b/ambari-web/latest/public/vite.svg
deleted file mode 100755
index e7b8dfb1b2..0000000000
--- a/ambari-web/latest/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; aria-hidden="true" role="img" 
class="iconify iconify--logos" width="31.88" height="32" 
preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient 
id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" 
y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" 
stop-color="#BD34FE"></stop></linearGradient><linearGradient 
id="IconifyId1813088fe1fbc01fb [...]
\ No newline at end of file
diff --git a/ambari-web/latest/src/Utils/persistedSettings.test.ts 
b/ambari-web/latest/src/Utils/persistedSettings.test.ts
index b3924ecc7c..424a8cc37b 100644
--- a/ambari-web/latest/src/Utils/persistedSettings.test.ts
+++ b/ambari-web/latest/src/Utils/persistedSettings.test.ts
@@ -17,7 +17,12 @@
  */
 
 import { describe, expect, it } from "vitest";
-import { parsePersistedValue, persistedPayload } from "./persistedSettings";
+import {
+  decodePersistedMap,
+  decodePersistedValue,
+  parsePersistedValue,
+  persistedPayload,
+} from "./persistedSettings";
 
 describe("persisted settings", () => {
   it("round trips booleans, strings, and objects as JSON values", () => {
@@ -39,4 +44,34 @@ describe("persisted settings", () => {
     expect(parsePersistedValue("null", { userName: "" })).toEqual({ userName: 
"" });
     expect(parsePersistedValue("not-json", "Browser")).toBe("Browser");
   });
+
+  it("decodes the JSON encoded values of an aggregate GET /persist response", 
() => {
+    const decoded = decodePersistedMap({
+      CLUSTER_CURRENT: '{"clusterCreationSteps":{"NAME":{"step":"NAME"}}}',
+      CLUSTER_STATE: 
'{"progressStatus":"PROVISIONING","stepName":"CONFIGURATION"}',
+      "wizard-data": '{"userName":"admin","controllerName":"clusterCreation"}',
+      USER_REDIRECTION_URL: "/main/admin/kerberos",
+    });
+
+    expect(decoded).toEqual({
+      CLUSTER_CURRENT: { clusterCreationSteps: { NAME: { step: "NAME" } } },
+      CLUSTER_STATE: { progressStatus: "PROVISIONING", stepName: 
"CONFIGURATION" },
+      "wizard-data": { userName: "admin", controllerName: "clusterCreation" },
+      USER_REDIRECTION_URL: "/main/admin/kerberos",
+    });
+  });
+
+  it("leaves already decoded values and non-map responses alone", () => {
+    const state = { stepName: "CONFIGURATION" };
+    expect(decodePersistedValue(state)).toBe(state);
+    expect(decodePersistedValue(undefined)).toBeUndefined();
+    expect(decodePersistedMap(undefined)).toBeUndefined();
+    expect(decodePersistedMap("")).toBe("");
+    expect(decodePersistedMap({ CLUSTER_STATE: state })).toEqual({ 
CLUSTER_STATE: state });
+  });
+
+  it("keeps decoded values usable by parsePersistedValue", () => {
+    const decoded = decodePersistedValue(persistedPayload({ enabled: false 
}).enabled);
+    expect(parsePersistedValue(decoded, true)).toBe(false);
+  });
 });
diff --git a/ambari-web/latest/src/Utils/persistedSettings.ts 
b/ambari-web/latest/src/Utils/persistedSettings.ts
index f66487225e..36e6223b97 100644
--- a/ambari-web/latest/src/Utils/persistedSettings.ts
+++ b/ambari-web/latest/src/Utils/persistedSettings.ts
@@ -31,6 +31,36 @@ export function parsePersistedValue<T>(value: unknown, 
fallback: T): T {
   }
 }
 
+/**
+ * GET /persist serializes Map<String, String>, so every value comes back as 
the
+ * raw string it was stored as - anything written with JSON.stringify stays
+ * encoded. (GET /persist/<key> returns that lone value as the whole body, 
which
+ * axios parses for us, which is why the per-key reads never needed this.) 
Decode
+ * a single value, leaving anything that was never JSON - USER_REDIRECTION_URL,
+ * for instance - as the plain string it is.
+ */
+export function decodePersistedValue(value: unknown): unknown {
+  if (typeof value !== "string") {
+    return value;
+  }
+  try {
+    return JSON.parse(value);
+  } catch {
+    return value;
+  }
+}
+
+/** Decode every value of the aggregate GET /persist response. */
+export function decodePersistedMap(data: unknown): unknown {
+  if (!data || typeof data !== "object" || Array.isArray(data)) {
+    return data;
+  }
+  return Object.fromEntries(
+    Object.entries(data as Record<string, unknown>)
+      .map(([key, value]) => [key, decodePersistedValue(value)]),
+  );
+}
+
 export function persistedPayload(values: Record<string, unknown>): 
Record<string, string> {
   return Object.fromEntries(
     Object.entries(values).map(([key, value]) => [key, JSON.stringify(value)]),
diff --git a/ambari-web/latest/src/api/clusterApi.test.ts 
b/ambari-web/latest/src/api/clusterApi.test.ts
index 37da1a3da1..6a9389c6f6 100644
--- a/ambari-web/latest/src/api/clusterApi.test.ts
+++ b/ambari-web/latest/src/api/clusterApi.test.ts
@@ -57,6 +57,27 @@ describe("cluster persisted data API", () => {
     });
   });
 
+  it("decodes the JSON encoded values the aggregate /persist response stores", 
async () => {
+    mocks.suppressedRequest.mockResolvedValue({
+      data: {
+        CLUSTER_CURRENT: '{"clusterCreationSteps":{"NAME":{"step":"NAME"}}}',
+        CLUSTER_STATE: 
'{"progressStatus":"PROVISIONING","stepName":"CONFIGURATION"}',
+        USER_REDIRECTION_URL: "/main/admin/kerberos",
+      },
+    });
+
+    await 
expect(ClusterApi.getPersistData("CLUSTER_CURRENT")).resolves.toEqual({
+      clusterCreationSteps: { NAME: { step: "NAME" } },
+    });
+    await expect(ClusterApi.getPersistData("CLUSTER_STATE")).resolves.toEqual({
+      progressStatus: "PROVISIONING",
+      stepName: "CONFIGURATION",
+    });
+    await 
expect(ClusterApi.getPersistData("USER_REDIRECTION_URL")).resolves.toBe(
+      "/main/admin/kerberos",
+    );
+  });
+
   it("deduplicates concurrent aggregate requests and treats missing keys as 
optional", async () => {
     let resolveRequest: (value: unknown) => void = () => undefined;
     mocks.suppressedRequest.mockReturnValue(new Promise((resolve) => {
diff --git a/ambari-web/latest/src/api/clusterApi.ts 
b/ambari-web/latest/src/api/clusterApi.ts
index 9fde7c0059..23244ed763 100644
--- a/ambari-web/latest/src/api/clusterApi.ts
+++ b/ambari-web/latest/src/api/clusterApi.ts
@@ -19,6 +19,7 @@
 import { apiPathSegment } from "./apiPath";
 import { set } from "lodash";
 import { ambariApi, supressErrorAmbariApi } from "./config/axiosConfig";
+import { decodePersistedMap } from "../Utils/persistedSettings";
 
 let pendingPersistDataRequest: Promise<any> | null = null;
 
@@ -27,7 +28,7 @@ const loadPersistData = async () => {
     pendingPersistDataRequest = supressErrorAmbariApi.request({
       url: "/persist",
       method: "GET",
-    }).then((response) => response.data).finally(() => {
+    }).then((response) => decodePersistedMap(response.data)).finally(() => {
       pendingPersistDataRequest = null;
     });
   }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to