feat: Add conditional API key display for auth and auto-login (#8684)
* ✨ Add IS_AUTO_LOGIN constant to handle auto login feature in NodeInputField and TableNodeCellRender components 🔧 Add loginLangflow utility function to facilitate login process in tests * ✨ (NodeInputField/index.tsx): introduce useIsAutoLogin hook to simplify logic for determining auto login status ✨ (tableNodeCellRender/index.tsx): introduce useIsAutoLogin hook to simplify logic for determining auto login status 📝 (use-is-auto-login.ts): add custom hook useIsAutoLogin to encapsulate logic for determining auto login status
This commit is contained in:
parent
797a7b6848
commit
0895ebbdcb
5 changed files with 131 additions and 6 deletions
|
|
@ -6,6 +6,7 @@ import {
|
||||||
CustomParameterLabel,
|
CustomParameterLabel,
|
||||||
getCustomParameterTitle,
|
getCustomParameterTitle,
|
||||||
} from "@/customization/components/custom-parameter";
|
} from "@/customization/components/custom-parameter";
|
||||||
|
import { useIsAutoLogin } from "@/hooks/use-is-auto-login";
|
||||||
import useAuthStore from "@/stores/authStore";
|
import useAuthStore from "@/stores/authStore";
|
||||||
import { cn } from "@/utils/utils";
|
import { cn } from "@/utils/utils";
|
||||||
import { useEffect, useMemo, useRef } from "react";
|
import { useEffect, useMemo, useRef } from "react";
|
||||||
|
|
@ -16,6 +17,7 @@ import {
|
||||||
DEFAULT_TOOLSET_PLACEHOLDER,
|
DEFAULT_TOOLSET_PLACEHOLDER,
|
||||||
FLEX_VIEW_TYPES,
|
FLEX_VIEW_TYPES,
|
||||||
ICON_STROKE_WIDTH,
|
ICON_STROKE_WIDTH,
|
||||||
|
IS_AUTO_LOGIN,
|
||||||
LANGFLOW_SUPPORTED_TYPES,
|
LANGFLOW_SUPPORTED_TYPES,
|
||||||
} from "../../../../constants/constants";
|
} from "../../../../constants/constants";
|
||||||
import useFlowStore from "../../../../stores/flowStore";
|
import useFlowStore from "../../../../stores/flowStore";
|
||||||
|
|
@ -44,13 +46,17 @@ export default function NodeInputField({
|
||||||
isToolMode = false,
|
isToolMode = false,
|
||||||
}: NodeInputFieldComponentType): JSX.Element {
|
}: NodeInputFieldComponentType): JSX.Element {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const isAuth = useAuthStore((state) => state.isAuthenticated);
|
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||||
|
const isAutoLogin = useIsAutoLogin();
|
||||||
|
const shouldDisplayApiKey = isAuthenticated && !isAutoLogin;
|
||||||
|
|
||||||
const { currentFlowId, currentFlowName } = useFlowStore(
|
const { currentFlowId, currentFlowName } = useFlowStore(
|
||||||
useShallow((state) => ({
|
useShallow((state) => ({
|
||||||
currentFlowId: state.currentFlow?.id,
|
currentFlowId: state.currentFlow?.id,
|
||||||
currentFlowName: state.currentFlow?.name,
|
currentFlowName: state.currentFlow?.name,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
const myData = useTypesStore((state) => state.data);
|
const myData = useTypesStore((state) => state.data);
|
||||||
const postTemplateValue = usePostTemplateValue({
|
const postTemplateValue = usePostTemplateValue({
|
||||||
node: data.node!,
|
node: data.node!,
|
||||||
|
|
@ -75,10 +81,10 @@ export default function NodeInputField({
|
||||||
flowId: currentFlowId ?? "",
|
flowId: currentFlowId ?? "",
|
||||||
nodeType: data?.type?.toLowerCase() ?? "",
|
nodeType: data?.type?.toLowerCase() ?? "",
|
||||||
flowName: currentFlowName ?? "",
|
flowName: currentFlowName ?? "",
|
||||||
isAuth,
|
isAuth: shouldDisplayApiKey!,
|
||||||
variableName: name,
|
variableName: name,
|
||||||
};
|
};
|
||||||
}, [data?.node?.id, isAuth, name]);
|
}, [data?.node?.id, shouldDisplayApiKey, name]);
|
||||||
|
|
||||||
useFetchDataOnMount(
|
useFetchDataOnMount(
|
||||||
data.node!,
|
data.node!,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import useHandleOnNewValue from "@/CustomNodes/hooks/use-handle-new-value";
|
||||||
import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class";
|
import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class";
|
||||||
import { ParameterRenderComponent } from "@/components/core/parameterRenderComponent";
|
import { ParameterRenderComponent } from "@/components/core/parameterRenderComponent";
|
||||||
import { NodeInfoType } from "@/components/core/parameterRenderComponent/types";
|
import { NodeInfoType } from "@/components/core/parameterRenderComponent/types";
|
||||||
|
import { IS_AUTO_LOGIN } from "@/constants/constants";
|
||||||
|
import { useIsAutoLogin } from "@/hooks/use-is-auto-login";
|
||||||
import useAuthStore from "@/stores/authStore";
|
import useAuthStore from "@/stores/authStore";
|
||||||
import useFlowStore from "@/stores/flowStore";
|
import useFlowStore from "@/stores/flowStore";
|
||||||
import { APIClassType } from "@/types/api";
|
import { APIClassType } from "@/types/api";
|
||||||
|
|
@ -17,7 +19,9 @@ export default function TableNodeCellRender({
|
||||||
const node = useFlowStore((state) => state.getNode(nodeId));
|
const node = useFlowStore((state) => state.getNode(nodeId));
|
||||||
const parameter = node?.data?.node?.template?.[parameterId];
|
const parameter = node?.data?.node?.template?.[parameterId];
|
||||||
const currentFlow = useFlowStore((state) => state.currentFlow);
|
const currentFlow = useFlowStore((state) => state.currentFlow);
|
||||||
const isAuth = useAuthStore((state) => state.isAuthenticated);
|
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
|
||||||
|
const isAutoLogin = useIsAutoLogin();
|
||||||
|
const shouldDisplayApiKey = isAuthenticated && !isAutoLogin;
|
||||||
|
|
||||||
const disabled = isTargetHandleConnected(
|
const disabled = isTargetHandleConnected(
|
||||||
edges,
|
edges,
|
||||||
|
|
@ -43,10 +47,10 @@ export default function TableNodeCellRender({
|
||||||
flowId: currentFlow?.id ?? "",
|
flowId: currentFlow?.id ?? "",
|
||||||
nodeType: node?.data?.type?.toLowerCase() ?? "",
|
nodeType: node?.data?.type?.toLowerCase() ?? "",
|
||||||
flowName: currentFlow?.name ?? "",
|
flowName: currentFlow?.name ?? "",
|
||||||
isAuth,
|
isAuth: shouldDisplayApiKey!,
|
||||||
variableName: parameterId,
|
variableName: parameterId,
|
||||||
};
|
};
|
||||||
}, [nodeId, isAuth, parameterId]);
|
}, [nodeId, shouldDisplayApiKey, parameterId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
parameter && (
|
parameter && (
|
||||||
|
|
|
||||||
8
src/frontend/src/hooks/use-is-auto-login.ts
Normal file
8
src/frontend/src/hooks/use-is-auto-login.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { IS_AUTO_LOGIN } from "@/constants/constants";
|
||||||
|
import useAuthStore from "@/stores/authStore";
|
||||||
|
|
||||||
|
export const useIsAutoLogin = (): boolean => {
|
||||||
|
const autoLogin = useAuthStore((state) => state.autoLogin);
|
||||||
|
const isAutoLoginEnv = IS_AUTO_LOGIN;
|
||||||
|
return autoLogin ?? isAutoLoginEnv;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { expect, test } from "@playwright/test";
|
||||||
|
import { adjustScreenView } from "../../utils/adjust-screen-view";
|
||||||
|
import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
|
||||||
|
import { extractAndCleanCode } from "../../utils/extract-and-clean-code";
|
||||||
|
import { loginLangflow } from "../../utils/login-langflow";
|
||||||
|
|
||||||
|
test(
|
||||||
|
"user must be able to see api key in webhook component when auto login is disabled",
|
||||||
|
{ tag: ["@release"] },
|
||||||
|
async ({ page }) => {
|
||||||
|
await page.route("**/api/v1/auto_login", (route) => {
|
||||||
|
route.fulfill({
|
||||||
|
status: 500,
|
||||||
|
contentType: "application/json",
|
||||||
|
body: JSON.stringify({
|
||||||
|
detail: { auto_login: false },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await loginLangflow(page);
|
||||||
|
|
||||||
|
await awaitBootstrapTest(page, { skipGoto: true });
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="blank-flow"]', {
|
||||||
|
timeout: 30000,
|
||||||
|
});
|
||||||
|
await page.getByTestId("blank-flow").click();
|
||||||
|
await page.getByTestId("sidebar-search-input").click();
|
||||||
|
|
||||||
|
await page.getByTestId("sidebar-search-input").fill("webhook");
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="dataWebhook"]', {
|
||||||
|
timeout: 3000,
|
||||||
|
});
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByTestId("dataWebhook")
|
||||||
|
.hover()
|
||||||
|
.then(async () => {
|
||||||
|
await page.getByTestId("add-component-button-webhook").click();
|
||||||
|
});
|
||||||
|
|
||||||
|
await adjustScreenView(page);
|
||||||
|
|
||||||
|
await page.getByTestId("title-Webhook").click();
|
||||||
|
|
||||||
|
await page.getByTestId("edit-button-modal").click();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByTestId("button_open_text_area_modal_str_edit_curl_advanced")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
const curl = await page.getByTestId("text-area-modal").inputValue();
|
||||||
|
|
||||||
|
expect(curl).toContain("x-api-key");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"user must be able to not see api key in webhook component when auto login is enabled",
|
||||||
|
{ tag: ["@release"] },
|
||||||
|
async ({ page }) => {
|
||||||
|
await awaitBootstrapTest(page);
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="blank-flow"]', {
|
||||||
|
timeout: 30000,
|
||||||
|
});
|
||||||
|
await page.getByTestId("blank-flow").click();
|
||||||
|
await page.getByTestId("sidebar-search-input").click();
|
||||||
|
|
||||||
|
await page.getByTestId("sidebar-search-input").fill("webhook");
|
||||||
|
|
||||||
|
await page.waitForSelector('[data-testid="dataWebhook"]', {
|
||||||
|
timeout: 3000,
|
||||||
|
});
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByTestId("dataWebhook")
|
||||||
|
.hover()
|
||||||
|
.then(async () => {
|
||||||
|
await page.getByTestId("add-component-button-webhook").click();
|
||||||
|
});
|
||||||
|
|
||||||
|
await adjustScreenView(page);
|
||||||
|
|
||||||
|
await page.getByTestId("title-Webhook").click();
|
||||||
|
|
||||||
|
await page.getByTestId("edit-button-modal").click();
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByTestId("button_open_text_area_modal_str_edit_curl_advanced")
|
||||||
|
.click();
|
||||||
|
|
||||||
|
const curl = await page.getByTestId("text-area-modal").inputValue();
|
||||||
|
|
||||||
|
expect(curl).not.toContain("x-api-key");
|
||||||
|
},
|
||||||
|
);
|
||||||
8
src/frontend/tests/utils/login-langflow.ts
Normal file
8
src/frontend/tests/utils/login-langflow.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { Page } from "playwright/test";
|
||||||
|
|
||||||
|
export const loginLangflow = async (page: Page) => {
|
||||||
|
await page.goto("/");
|
||||||
|
await page.getByPlaceholder("Username").fill("langflow");
|
||||||
|
await page.getByPlaceholder("Password").fill("langflow");
|
||||||
|
await page.getByRole("button", { name: "Sign In" }).click();
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue