mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-09 14:57:59 -05:00
chore: Move user's analytics consent to the backend (#6505)
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { AnalyticsConsentFormModal } from "#/components/features/analytics/analytics-consent-form-modal";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { SettingsProvider } from "#/context/settings-context";
|
||||
import { AuthProvider } from "#/context/auth-context";
|
||||
|
||||
describe("AnalyticsConsentFormModal", () => {
|
||||
it("should call saveUserSettings with default settings on confirm reset settings", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onCloseMock = vi.fn();
|
||||
const saveUserSettingsSpy = vi.spyOn(OpenHands, "saveSettings");
|
||||
|
||||
render(<AnalyticsConsentFormModal onClose={onCloseMock} />, {
|
||||
wrapper: ({ children }) => (
|
||||
<AuthProvider>
|
||||
<QueryClientProvider client={new QueryClient()}>
|
||||
<SettingsProvider>{children}</SettingsProvider>
|
||||
</QueryClientProvider>
|
||||
</AuthProvider>
|
||||
),
|
||||
});
|
||||
|
||||
const confirmButton = screen.getByTestId("confirm-preferences");
|
||||
await user.click(confirmButton);
|
||||
|
||||
expect(saveUserSettingsSpy).toHaveBeenCalledWith({
|
||||
user_consents_to_analytics: true,
|
||||
agent: "CodeActAgent",
|
||||
confirmation_mode: false,
|
||||
enable_default_condenser: false,
|
||||
github_token: undefined,
|
||||
language: "en",
|
||||
llm_api_key: undefined,
|
||||
llm_base_url: "",
|
||||
llm_model: "anthropic/claude-3-5-sonnet-20241022",
|
||||
remote_runtime_resource_factor: 1,
|
||||
security_analyzer: "",
|
||||
unset_github_token: undefined,
|
||||
});
|
||||
expect(onCloseMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -82,6 +82,10 @@ describe("Sidebar", () => {
|
||||
within(accountSettingsModal).getByLabelText(/GITHUB\$TOKEN_LABEL/i);
|
||||
await user.type(tokenInput, "new-token");
|
||||
|
||||
const analyticsConsentInput =
|
||||
within(accountSettingsModal).getByTestId("analytics-consent");
|
||||
await user.click(analyticsConsentInput);
|
||||
|
||||
const saveButton =
|
||||
within(accountSettingsModal).getByTestId("save-settings");
|
||||
await user.click(saveButton);
|
||||
@@ -96,6 +100,7 @@ describe("Sidebar", () => {
|
||||
llm_model: "anthropic/claude-3-5-sonnet-20241022",
|
||||
remote_runtime_resource_factor: 1,
|
||||
security_analyzer: "",
|
||||
user_consents_to_analytics: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,16 +1,74 @@
|
||||
import { screen, waitFor } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { renderWithProviders } from "test-utils";
|
||||
import { AccountSettingsModal } from "#/components/shared/modals/account-settings/account-settings-modal";
|
||||
import { MOCK_DEFAULT_USER_SETTINGS } from "#/mocks/handlers";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import * as ConsentHandlers from "#/utils/handle-capture-consent";
|
||||
|
||||
describe("AccountSettingsModal", () => {
|
||||
const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
|
||||
const saveSettingsSpy = vi.spyOn(OpenHands, "saveSettings");
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it.skip("should set the appropriate user analytics consent default", async () => {
|
||||
getSettingsSpy.mockResolvedValue({
|
||||
...MOCK_DEFAULT_USER_SETTINGS,
|
||||
user_consents_to_analytics: true,
|
||||
});
|
||||
renderWithProviders(<AccountSettingsModal onClose={() => {}} />);
|
||||
|
||||
const analyticsConsentInput = screen.getByTestId("analytics-consent");
|
||||
await waitFor(() => expect(analyticsConsentInput).toBeChecked());
|
||||
});
|
||||
|
||||
it("should save the users consent to analytics when saving account settings", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(<AccountSettingsModal onClose={() => {}} />);
|
||||
|
||||
const analyticsConsentInput = screen.getByTestId("analytics-consent");
|
||||
await user.click(analyticsConsentInput);
|
||||
|
||||
const saveButton = screen.getByTestId("save-settings");
|
||||
await user.click(saveButton);
|
||||
|
||||
expect(saveSettingsSpy).toHaveBeenCalledWith({
|
||||
agent: "CodeActAgent",
|
||||
confirmation_mode: false,
|
||||
enable_default_condenser: false,
|
||||
language: "en",
|
||||
llm_base_url: "",
|
||||
llm_model: "anthropic/claude-3-5-sonnet-20241022",
|
||||
remote_runtime_resource_factor: 1,
|
||||
security_analyzer: "",
|
||||
user_consents_to_analytics: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("should call handleCaptureConsent with the analytics consent value if the save is successful", async () => {
|
||||
const user = userEvent.setup();
|
||||
const handleCaptureConsentSpy = vi.spyOn(
|
||||
ConsentHandlers,
|
||||
"handleCaptureConsent",
|
||||
);
|
||||
renderWithProviders(<AccountSettingsModal onClose={() => {}} />);
|
||||
|
||||
const analyticsConsentInput = screen.getByTestId("analytics-consent");
|
||||
await user.click(analyticsConsentInput);
|
||||
|
||||
const saveButton = screen.getByTestId("save-settings");
|
||||
await user.click(saveButton);
|
||||
|
||||
expect(handleCaptureConsentSpy).toHaveBeenCalledWith(true);
|
||||
|
||||
await user.click(analyticsConsentInput);
|
||||
await user.click(saveButton);
|
||||
|
||||
expect(handleCaptureConsentSpy).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("should send all settings data when saving account settings", async () => {
|
||||
@@ -39,11 +97,11 @@ describe("AccountSettingsModal", () => {
|
||||
llm_model: "anthropic/claude-3-5-sonnet-20241022",
|
||||
remote_runtime_resource_factor: 1,
|
||||
security_analyzer: "",
|
||||
user_consents_to_analytics: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("should render a checkmark and not the input if the github token is set", async () => {
|
||||
const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
|
||||
getSettingsSpy.mockResolvedValue({
|
||||
...MOCK_DEFAULT_USER_SETTINGS,
|
||||
github_token_is_set: true,
|
||||
@@ -61,7 +119,6 @@ describe("AccountSettingsModal", () => {
|
||||
|
||||
it("should send an unset github token property when pressing disconnect", async () => {
|
||||
const user = userEvent.setup();
|
||||
const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
|
||||
getSettingsSpy.mockResolvedValue({
|
||||
...MOCK_DEFAULT_USER_SETTINGS,
|
||||
github_token_is_set: true,
|
||||
@@ -86,7 +143,6 @@ describe("AccountSettingsModal", () => {
|
||||
|
||||
it("should not unset the github token when changing the language", async () => {
|
||||
const user = userEvent.setup();
|
||||
const getSettingsSpy = vi.spyOn(OpenHands, "getSettings");
|
||||
getSettingsSpy.mockResolvedValue({
|
||||
...MOCK_DEFAULT_USER_SETTINGS,
|
||||
github_token_is_set: true,
|
||||
@@ -111,6 +167,7 @@ describe("AccountSettingsModal", () => {
|
||||
llm_model: "anthropic/claude-3-5-sonnet-20241022",
|
||||
remote_runtime_resource_factor: 1,
|
||||
security_analyzer: "",
|
||||
user_consents_to_analytics: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -74,7 +74,6 @@ describe("frontend/routes/_oh", () => {
|
||||
// The user has not consented to tracking
|
||||
const consentForm = await screen.findByTestId("user-capture-consent-form");
|
||||
expect(handleCaptureConsentSpy).not.toHaveBeenCalled();
|
||||
expect(localStorage.getItem("analytics-consent")).toBeNull();
|
||||
|
||||
const submitButton = within(consentForm).getByRole("button", {
|
||||
name: /confirm preferences/i,
|
||||
@@ -83,7 +82,6 @@ describe("frontend/routes/_oh", () => {
|
||||
|
||||
// The user has now consented to tracking
|
||||
expect(handleCaptureConsentSpy).toHaveBeenCalledWith(true);
|
||||
expect(localStorage.getItem("analytics-consent")).toBe("true");
|
||||
expect(
|
||||
screen.queryByTestId("user-capture-consent-form"),
|
||||
).not.toBeInTheDocument();
|
||||
@@ -106,17 +104,6 @@ describe("frontend/routes/_oh", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should not render the user consent form if the user has already made a decision", async () => {
|
||||
localStorage.setItem("analytics-consent", "true");
|
||||
renderWithProviders(<RouteStub />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByTestId("user-capture-consent-form"),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Likely failing due to how tokens are now handled in context. Move to e2e tests
|
||||
it.skip("should render a new project button if a token is set", async () => {
|
||||
localStorage.setItem("token", "test-token");
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
} from "#/components/shared/modals/confirmation-modals/base-modal";
|
||||
import { ModalBackdrop } from "#/components/shared/modals/modal-backdrop";
|
||||
import { ModalBody } from "#/components/shared/modals/modal-body";
|
||||
import { useCurrentSettings } from "#/context/settings-context";
|
||||
import { handleCaptureConsent } from "#/utils/handle-capture-consent";
|
||||
|
||||
interface AnalyticsConsentFormModalProps {
|
||||
@@ -14,13 +15,21 @@ interface AnalyticsConsentFormModalProps {
|
||||
export function AnalyticsConsentFormModal({
|
||||
onClose,
|
||||
}: AnalyticsConsentFormModalProps) {
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
const { saveUserSettings } = useCurrentSettings();
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.currentTarget);
|
||||
const analytics = formData.get("analytics") === "on";
|
||||
|
||||
handleCaptureConsent(analytics);
|
||||
localStorage.setItem("analytics-consent", analytics.toString());
|
||||
await saveUserSettings(
|
||||
{ user_consents_to_analytics: analytics },
|
||||
{
|
||||
onSuccess: () => {
|
||||
handleCaptureConsent(analytics);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
onClose();
|
||||
};
|
||||
@@ -46,6 +55,7 @@ export function AnalyticsConsentFormModal({
|
||||
</label>
|
||||
|
||||
<ModalButton
|
||||
testId="confirm-preferences"
|
||||
type="submit"
|
||||
text="Confirm Preferences"
|
||||
className="bg-primary text-white w-full hover:opacity-80"
|
||||
|
||||
@@ -15,25 +15,21 @@ import { useConfig } from "#/hooks/query/use-config";
|
||||
import { useCurrentSettings } from "#/context/settings-context";
|
||||
import { GitHubTokenInput } from "./github-token-input";
|
||||
import { PostSettings } from "#/types/settings";
|
||||
import { useGitHubUser } from "#/hooks/query/use-github-user";
|
||||
|
||||
interface AccountSettingsFormProps {
|
||||
onClose: () => void;
|
||||
selectedLanguage: string;
|
||||
gitHubError: boolean;
|
||||
analyticsConsent: string | null;
|
||||
}
|
||||
|
||||
export function AccountSettingsForm({
|
||||
onClose,
|
||||
selectedLanguage,
|
||||
gitHubError,
|
||||
analyticsConsent,
|
||||
}: AccountSettingsFormProps) {
|
||||
export function AccountSettingsForm({ onClose }: AccountSettingsFormProps) {
|
||||
const { isError: isGitHubError } = useGitHubUser();
|
||||
const { data: config } = useConfig();
|
||||
const { saveUserSettings, settings } = useCurrentSettings();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const githubTokenIsSet = !!settings?.GITHUB_TOKEN_IS_SET;
|
||||
const analyticsConsentValue = !!settings?.USER_CONSENTS_TO_ANALYTICS;
|
||||
const selectedLanguage = settings?.LANGUAGE || "en";
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
@@ -44,6 +40,7 @@ export function AccountSettingsForm({
|
||||
const analytics = formData.get("analytics")?.toString() === "on";
|
||||
|
||||
const newSettings: Partial<PostSettings> = {};
|
||||
newSettings.user_consents_to_analytics = analytics;
|
||||
|
||||
if (ghToken) newSettings.github_token = ghToken;
|
||||
|
||||
@@ -57,11 +54,11 @@ export function AccountSettingsForm({
|
||||
if (languageKey) newSettings.LANGUAGE = languageKey;
|
||||
}
|
||||
|
||||
await saveUserSettings(newSettings);
|
||||
|
||||
handleCaptureConsent(analytics);
|
||||
const ANALYTICS = analytics.toString();
|
||||
localStorage.setItem("analytics-consent", ANALYTICS);
|
||||
await saveUserSettings(newSettings, {
|
||||
onSuccess: () => {
|
||||
handleCaptureConsent(analytics);
|
||||
},
|
||||
});
|
||||
|
||||
onClose();
|
||||
};
|
||||
@@ -117,12 +114,12 @@ export function AccountSettingsForm({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{gitHubError && (
|
||||
{isGitHubError && (
|
||||
<p className="text-danger text-xs">
|
||||
{t(I18nKey.GITHUB$TOKEN_INVALID)}
|
||||
</p>
|
||||
)}
|
||||
{githubTokenIsSet && !gitHubError && (
|
||||
{githubTokenIsSet && !isGitHubError && (
|
||||
<ModalButton
|
||||
testId="disconnect-github"
|
||||
variant="text-like"
|
||||
@@ -135,9 +132,10 @@ export function AccountSettingsForm({
|
||||
|
||||
<label className="flex gap-2 items-center self-start">
|
||||
<input
|
||||
data-testid="analytics-consent"
|
||||
name="analytics"
|
||||
type="checkbox"
|
||||
defaultChecked={analyticsConsent === "true"}
|
||||
defaultChecked={analyticsConsentValue}
|
||||
/>
|
||||
{t(I18nKey.ANALYTICS$ENABLE)}
|
||||
</label>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { useGitHubUser } from "#/hooks/query/use-github-user";
|
||||
import { useSettings } from "#/hooks/query/use-settings";
|
||||
import { ModalBackdrop } from "../modal-backdrop";
|
||||
import { AccountSettingsForm } from "./account-settings-form";
|
||||
|
||||
@@ -8,20 +6,9 @@ interface AccountSettingsModalProps {
|
||||
}
|
||||
|
||||
export function AccountSettingsModal({ onClose }: AccountSettingsModalProps) {
|
||||
const user = useGitHubUser();
|
||||
const { data: settings } = useSettings();
|
||||
|
||||
// FIXME: Bad practice to use localStorage directly
|
||||
const analyticsConsent = localStorage.getItem("analytics-consent");
|
||||
|
||||
return (
|
||||
<ModalBackdrop onClose={onClose}>
|
||||
<AccountSettingsForm
|
||||
onClose={onClose}
|
||||
selectedLanguage={settings?.LANGUAGE || "en"}
|
||||
gitHubError={user.isError}
|
||||
analyticsConsent={analyticsConsent}
|
||||
/>
|
||||
<AccountSettingsForm onClose={onClose} />
|
||||
</ModalBackdrop>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
import React from "react";
|
||||
import { MutateOptions } from "@tanstack/react-query";
|
||||
import { useSettings } from "#/hooks/query/use-settings";
|
||||
import { useSaveSettings } from "#/hooks/mutation/use-save-settings";
|
||||
import { PostSettings, Settings } from "#/types/settings";
|
||||
|
||||
type SaveUserSettingsConfig = {
|
||||
onSuccess: MutateOptions<void, Error, Partial<PostSettings>>["onSuccess"];
|
||||
};
|
||||
|
||||
interface SettingsContextType {
|
||||
saveUserSettings: (newSettings: Partial<PostSettings>) => Promise<void>;
|
||||
saveUserSettings: (
|
||||
newSettings: Partial<PostSettings>,
|
||||
config?: SaveUserSettingsConfig,
|
||||
) => Promise<void>;
|
||||
settings: Settings | undefined;
|
||||
}
|
||||
|
||||
@@ -20,7 +28,10 @@ export function SettingsProvider({ children }: SettingsProviderProps) {
|
||||
const { data: userSettings } = useSettings();
|
||||
const { mutateAsync: saveSettings } = useSaveSettings();
|
||||
|
||||
const saveUserSettings = async (newSettings: Partial<PostSettings>) => {
|
||||
const saveUserSettings = async (
|
||||
newSettings: Partial<PostSettings>,
|
||||
config?: SaveUserSettingsConfig,
|
||||
) => {
|
||||
const updatedSettings: Partial<PostSettings> = {
|
||||
...userSettings,
|
||||
...newSettings,
|
||||
@@ -30,7 +41,7 @@ export function SettingsProvider({ children }: SettingsProviderProps) {
|
||||
delete updatedSettings.LLM_API_KEY;
|
||||
}
|
||||
|
||||
await saveSettings(updatedSettings);
|
||||
await saveSettings(updatedSettings, { onSuccess: config?.onSuccess });
|
||||
};
|
||||
|
||||
const value = React.useMemo(
|
||||
|
||||
@@ -16,6 +16,7 @@ const saveSettingsMutationFn = async (settings: Partial<PostSettings>) => {
|
||||
github_token: settings.github_token,
|
||||
unset_github_token: settings.unset_github_token,
|
||||
enable_default_condenser: settings.ENABLE_DEFAULT_CONDENSER,
|
||||
user_consents_to_analytics: settings.user_consents_to_analytics,
|
||||
};
|
||||
|
||||
await OpenHands.saveSettings(apiSettings);
|
||||
|
||||
@@ -19,6 +19,7 @@ const getSettingsQueryFn = async () => {
|
||||
REMOTE_RUNTIME_RESOURCE_FACTOR: apiSettings.remote_runtime_resource_factor,
|
||||
GITHUB_TOKEN_IS_SET: apiSettings.github_token_is_set,
|
||||
ENABLE_DEFAULT_CONDENSER: apiSettings.enable_default_condenser,
|
||||
USER_CONSENTS_TO_ANALYTICS: apiSettings.user_consents_to_analytics,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
34
frontend/src/hooks/use-migrate-user-consent.ts
Normal file
34
frontend/src/hooks/use-migrate-user-consent.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from "react";
|
||||
import { useCurrentSettings } from "#/context/settings-context";
|
||||
import { handleCaptureConsent } from "#/utils/handle-capture-consent";
|
||||
|
||||
export const useMigrateUserConsent = () => {
|
||||
const { saveUserSettings } = useCurrentSettings();
|
||||
|
||||
/**
|
||||
* Migrate user consent to the settings store on the server.
|
||||
*/
|
||||
const migrateUserConsent = React.useCallback(
|
||||
async (args?: { handleAnalyticsWasPresentInLocalStorage: () => void }) => {
|
||||
const userAnalyticsConsent = localStorage.getItem("analytics-consent");
|
||||
|
||||
if (userAnalyticsConsent) {
|
||||
args?.handleAnalyticsWasPresentInLocalStorage();
|
||||
|
||||
await saveUserSettings(
|
||||
{ user_consents_to_analytics: userAnalyticsConsent === "true" },
|
||||
{
|
||||
onSuccess: () => {
|
||||
handleCaptureConsent(userAnalyticsConsent === "true");
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
localStorage.removeItem("analytics-consent");
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return { migrateUserConsent };
|
||||
};
|
||||
@@ -19,6 +19,7 @@ export const MOCK_DEFAULT_USER_SETTINGS: ApiSettings | PostApiSettings = {
|
||||
DEFAULT_SETTINGS.REMOTE_RUNTIME_RESOURCE_FACTOR,
|
||||
github_token_is_set: DEFAULT_SETTINGS.GITHUB_TOKEN_IS_SET,
|
||||
enable_default_condenser: DEFAULT_SETTINGS.ENABLE_DEFAULT_CONDENSER,
|
||||
user_consents_to_analytics: DEFAULT_SETTINGS.USER_CONSENTS_TO_ANALYTICS,
|
||||
};
|
||||
|
||||
const MOCK_USER_PREFERENCES: {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { WaitlistModal } from "#/components/features/waitlist/waitlist-modal";
|
||||
import { AnalyticsConsentFormModal } from "#/components/features/analytics/analytics-consent-form-modal";
|
||||
import { useSettings } from "#/hooks/query/use-settings";
|
||||
import { useAuth } from "#/context/auth-context";
|
||||
import { useMigrateUserConsent } from "#/hooks/use-migrate-user-consent";
|
||||
|
||||
export function ErrorBoundary() {
|
||||
const error = useRouteError();
|
||||
@@ -45,9 +46,10 @@ export function ErrorBoundary() {
|
||||
export default function MainApp() {
|
||||
const { githubTokenIsSet } = useAuth();
|
||||
const { data: settings } = useSettings();
|
||||
const { migrateUserConsent } = useMigrateUserConsent();
|
||||
|
||||
const [consentFormIsOpen, setConsentFormIsOpen] = React.useState(
|
||||
!localStorage.getItem("analytics-consent"),
|
||||
settings.USER_CONSENTS_TO_ANALYTICS === null,
|
||||
);
|
||||
|
||||
const config = useConfig();
|
||||
@@ -68,6 +70,15 @@ export default function MainApp() {
|
||||
}
|
||||
}, [settings?.LANGUAGE]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Migrate user consent to the server if it was previously stored in localStorage
|
||||
migrateUserConsent({
|
||||
handleAnalyticsWasPresentInLocalStorage: () => {
|
||||
setConsentFormIsOpen(false);
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
const userIsAuthed = !!isAuthed && !authError;
|
||||
const renderWaitlistModal =
|
||||
!isFetchingAuth && !userIsAuthed && config.data?.APP_MODE === "saas";
|
||||
@@ -95,7 +106,9 @@ export default function MainApp() {
|
||||
|
||||
{config.data?.APP_MODE === "oss" && consentFormIsOpen && (
|
||||
<AnalyticsConsentFormModal
|
||||
onClose={() => setConsentFormIsOpen(false)}
|
||||
onClose={() => {
|
||||
setConsentFormIsOpen(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ export const DEFAULT_SETTINGS: Settings = {
|
||||
REMOTE_RUNTIME_RESOURCE_FACTOR: 1,
|
||||
GITHUB_TOKEN_IS_SET: false,
|
||||
ENABLE_DEFAULT_CONDENSER: false,
|
||||
USER_CONSENTS_TO_ANALYTICS: null,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,7 @@ export type Settings = {
|
||||
REMOTE_RUNTIME_RESOURCE_FACTOR: number;
|
||||
GITHUB_TOKEN_IS_SET: boolean;
|
||||
ENABLE_DEFAULT_CONDENSER: boolean;
|
||||
USER_CONSENTS_TO_ANALYTICS: boolean | null;
|
||||
};
|
||||
|
||||
export type ApiSettings = {
|
||||
@@ -22,14 +23,17 @@ export type ApiSettings = {
|
||||
remote_runtime_resource_factor: number;
|
||||
github_token_is_set: boolean;
|
||||
enable_default_condenser: boolean;
|
||||
user_consents_to_analytics: boolean | null;
|
||||
};
|
||||
|
||||
export type PostSettings = Settings & {
|
||||
github_token: string;
|
||||
unset_github_token: boolean;
|
||||
user_consents_to_analytics: boolean | null;
|
||||
};
|
||||
|
||||
export type PostApiSettings = ApiSettings & {
|
||||
github_token: string;
|
||||
unset_github_token: boolean;
|
||||
user_consents_to_analytics: boolean | null;
|
||||
};
|
||||
|
||||
@@ -33,7 +33,6 @@ test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem("FEATURE_MULTI_CONVERSATION_UI", "true");
|
||||
localStorage.setItem("analytics-consent", "true");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@ const dirname = path.dirname(filename);
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem("analytics-consent", "true");
|
||||
});
|
||||
});
|
||||
|
||||
test("should redirect to /conversations after uploading a project zip", async ({
|
||||
|
||||
@@ -2,9 +2,6 @@ import test, { expect, Page } from "@playwright/test";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.evaluate(() => {
|
||||
localStorage.setItem("analytics-consent", "true");
|
||||
});
|
||||
});
|
||||
|
||||
const selectGpt4o = async (page: Page) => {
|
||||
|
||||
@@ -85,6 +85,11 @@ async def store_settings(
|
||||
if settings.github_token is None:
|
||||
settings.github_token = existing_settings.github_token
|
||||
|
||||
if settings.user_consents_to_analytics is None:
|
||||
settings.user_consents_to_analytics = (
|
||||
existing_settings.user_consents_to_analytics
|
||||
)
|
||||
|
||||
response = JSONResponse(
|
||||
status_code=status.HTTP_200_OK,
|
||||
content={'message': 'Settings stored'},
|
||||
|
||||
@@ -23,6 +23,7 @@ class Settings(BaseModel):
|
||||
remote_runtime_resource_factor: int | None = None
|
||||
github_token: str | None = None
|
||||
enable_default_condenser: bool = False
|
||||
user_consents_to_analytics: bool | None = None
|
||||
|
||||
@field_serializer('llm_api_key')
|
||||
def llm_api_key_serializer(self, llm_api_key: SecretStr, info: SerializationInfo):
|
||||
|
||||
Reference in New Issue
Block a user