From c3ef6060afcaba0c5561b58abac6142e8bdbec2a Mon Sep 17 00:00:00 2001
From: github-automation-metabase
 <166700802+github-automation-metabase@users.noreply.github.com>
Date: Fri, 15 Nov 2024 11:37:38 -0500
Subject: [PATCH] fix(sdk): remove `Cannot update a component XX while
 rendering a (#50078) (#50084)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(sdk): remove `Cannot update a component XX while rendering a
different component` error

* test(sdk): introduce jest test to make sure api.basename is set before
loading the locale

Co-authored-by: Nicolò Pretto <info@npretto.com>
---
 .../hooks/private/use-init-data.ts            | 12 ++++--
 .../test/auth-flow.unit.spec.tsx              | 42 ++++++++++++++++---
 2 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/enterprise/frontend/src/embedding-sdk/hooks/private/use-init-data.ts b/enterprise/frontend/src/embedding-sdk/hooks/private/use-init-data.ts
index 290bc636359..b682bc6e2a2 100644
--- a/enterprise/frontend/src/embedding-sdk/hooks/private/use-init-data.ts
+++ b/enterprise/frontend/src/embedding-sdk/hooks/private/use-init-data.ts
@@ -1,4 +1,4 @@
-import { useRef } from "react";
+import { useEffect, useRef } from "react";
 import { useMount } from "react-use";
 import _ from "underscore";
 
@@ -37,9 +37,13 @@ export const useInitData = ({ config }: InitDataLoaderParameters) => {
   if (api.basename !== config.metabaseInstanceUrl) {
     api.basename = config.metabaseInstanceUrl;
   }
-  if (config.fetchRequestToken !== fetchRefreshTokenFnFromStore) {
-    dispatch(setFetchRefreshTokenFn(config.fetchRequestToken ?? null));
-  }
+
+  useEffect(() => {
+    if (config.fetchRequestToken !== fetchRefreshTokenFnFromStore) {
+      // This needs to be a useEffect to avoid the `Cannot update a component XX while rendering a different component` error
+      dispatch(setFetchRefreshTokenFn(config.fetchRequestToken ?? null));
+    }
+  }, [config.fetchRequestToken, fetchRefreshTokenFnFromStore, dispatch]);
 
   useMount(() => {
     if (hasBeenInitialized.current) {
diff --git a/enterprise/frontend/src/embedding-sdk/test/auth-flow.unit.spec.tsx b/enterprise/frontend/src/embedding-sdk/test/auth-flow.unit.spec.tsx
index 439528e97d2..4af42646cc9 100644
--- a/enterprise/frontend/src/embedding-sdk/test/auth-flow.unit.spec.tsx
+++ b/enterprise/frontend/src/embedding-sdk/test/auth-flow.unit.spec.tsx
@@ -34,12 +34,19 @@ const MOCK_SESSION = {
 
 const MOCK_CARD = createMockCard({ id: 1 });
 
-const setup = (sdkConfig: SDKConfig) => {
+const setup = ({
+  sdkConfig,
+  locale,
+}: {
+  sdkConfig: SDKConfig;
+  locale?: string;
+}) => {
   return render(
     <MetabaseProvider
       config={{
         ...sdkConfig,
       }}
+      locale={locale}
     >
       <StaticQuestion questionId={1} />
     </MetabaseProvider>,
@@ -51,7 +58,7 @@ const getLastAuthProviderApiCall = () => fetchMock.lastCall(AUTH_PROVIDER_URL);
 const getLastCardQueryApiCall = () =>
   fetchMock.lastCall(`${METABASE_INSTANCE_URL}/api/card/${MOCK_CARD.id}/query`);
 
-describe("SDK auth flow", () => {
+describe("SDK auth and init flow", () => {
   beforeEach(() => {
     fetchMock.reset();
     fetchMock.get(AUTH_PROVIDER_URL, {
@@ -79,7 +86,7 @@ describe("SDK auth flow", () => {
       authProviderUri: AUTH_PROVIDER_URL,
     });
 
-    const { rerender } = setup(sdkConfig);
+    const { rerender } = setup({ sdkConfig });
 
     expect(fetchMock.calls(AUTH_PROVIDER_URL)).toHaveLength(1);
 
@@ -111,7 +118,7 @@ describe("SDK auth flow", () => {
         authProviderUri: AUTH_PROVIDER_URL,
       });
 
-      setup(sdkConfig);
+      setup({ sdkConfig });
 
       await waitForRequest(() => getLastAuthProviderApiCall());
       expect(getLastAuthProviderApiCall()![1]).toMatchObject({
@@ -142,7 +149,7 @@ describe("SDK auth flow", () => {
         fetchRequestToken: customFetchFunction,
       });
 
-      setup(sdkConfig);
+      setup({ sdkConfig });
 
       expect(customFetchFunction).toHaveBeenCalledWith(AUTH_PROVIDER_URL);
 
@@ -169,7 +176,7 @@ describe("SDK auth flow", () => {
         apiKey: MOCK_API_KEY,
       });
 
-      setup(sdkConfig);
+      setup({ sdkConfig });
 
       await waitForRequest(() => getLastUserApiCall());
       expect(getLastUserApiCall()![1]).toMatchObject({
@@ -187,4 +194,27 @@ describe("SDK auth flow", () => {
       });
     });
   });
+
+  describe("locale", () => {
+    it("should load the locale from the correct url", () => {
+      const metabaseInstanceUrl = "http://metabase:3000";
+
+      // This can happen if the request is made before api.basename is set
+      const wrongPath = "/app/locales/de.json";
+      const correctPath = `${metabaseInstanceUrl}/app/locales/de.json`;
+
+      fetchMock.get(wrongPath, 200);
+      fetchMock.get(correctPath, 200);
+
+      const sdkConfig = defineEmbeddingSdkConfig({
+        metabaseInstanceUrl: metabaseInstanceUrl,
+        apiKey: MOCK_API_KEY,
+      });
+
+      setup({ sdkConfig, locale: "de" });
+
+      expect(fetchMock.calls(wrongPath)).toHaveLength(0);
+      expect(fetchMock.calls(correctPath)).toHaveLength(1);
+    });
+  });
 });
-- 
GitLab