Skip to content

Commit

Permalink
chore: refactor cloud panel a bit (#1137)
Browse files Browse the repository at this point in the history
* chore: refactor cloud panel a bit

* chore: api key -> secret

* chore: do not fetch again

* chore: format

* fix: format
  • Loading branch information
itsjoeoui authored Mar 19, 2024
1 parent cb2199b commit 9f5a816
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

@node_preflight
def preflight():
api_key = get_env_var("FLOJOY_CLOUD_KEY")
api_key = get_env_var("FLOJOY_CLOUD_WORKSPACE_SECRET")

if api_key is None:
raise KeyError(
Expand All @@ -41,7 +41,7 @@ def FLOJOY_CLOUD_DOWNLOAD(
The downloaded DataContainer will be returned as it is.
"""

api_key = get_env_var("FLOJOY_CLOUD_KEY")
api_key = get_env_var("FLOJOY_CLOUD_WORKSPACE_SECRET")

if api_key is None:
raise KeyError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

@node_preflight
def preflight():
api_key = get_env_var("FLOJOY_CLOUD_KEY")
api_key = get_env_var("FLOJOY_CLOUD_WORKSPACE_SECRET")

if api_key is None:
raise KeyError(
Expand Down Expand Up @@ -65,7 +65,7 @@ def FLOJOY_CLOUD_UPLOAD(
The input DataContainer will be returned as it is.
"""

api_key = get_env_var("FLOJOY_CLOUD_KEY")
api_key = get_env_var("FLOJOY_CLOUD_WORKSPACE_SECRET")

if api_key is None:
raise KeyError(
Expand Down
6 changes: 3 additions & 3 deletions captain/routes/cloud.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import json

from fastapi import APIRouter, Response
from flojoy_cloud.client import FlojoyCloud
from flojoy.env_var import get_env_var
from flojoy_cloud.client import FlojoyCloud

router = APIRouter(tags=["cloud"])

Expand All @@ -12,11 +12,11 @@ async def get_cloud_projects():
"""
Get all projects from the Flojoy Cloud.
"""
workspace_secret = get_env_var("FLOJOY_CLOUD_KEY")
workspace_secret = get_env_var("FLOJOY_CLOUD_WORKSPACE_SECRET")
if workspace_secret is None:
return Response(status_code=401, content=json.dumps([]))

cloud = FlojoyCloud(workspace_secret=get_env_var("FLOJOY_CLOUD_KEY"))
cloud = FlojoyCloud(workspace_secret=workspace_secret)

projects = cloud.get_all_projects(
""
Expand Down
4 changes: 3 additions & 1 deletion captain/utils/test_sequencer/run_test_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,9 @@ def _case_test_upload(node: TestNode, hardware_id: str, project_id: str) -> Extr
if node.completion_time is None:
raise Exception(f"{node.id}: Can't upload a test that wasn't run")
if node.export_to_cloud:
cloud = FlojoyCloud(workspace_secret=get_env_var("FLOJOY_CLOUD_KEY"))
cloud = FlojoyCloud(
workspace_secret=get_env_var("FLOJOY_CLOUD_WORKSPACE_SECRET")
)

def reverse_id(test_name) -> str:
tests = cloud.get_all_tests_by_project_id(project_id)
Expand Down
6 changes: 4 additions & 2 deletions playwright-test/10_env_modal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@ test.describe("Environment modal", () => {
await window.getByTestId(Selectors.flojoyCloudApiSubmit).click();

try {
// Expect "FLOJOY_CLOUD_KEY" to be listed in the modal
await expect(window.getByText("FLOJOY_CLOUD_KEY")).toBeVisible({
// Expect "FLOJOY_CLOUD_WORKSPACE_SECRET" to be listed in the modal
await expect(
window.getByText("FLOJOY_CLOUD_WORKSPACE_SECRET"),
).toBeVisible({
timeout: 20000,
});
} catch (error) {
Expand Down
14 changes: 9 additions & 5 deletions src/renderer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import { HashRouter } from "react-router-dom";
import { AuthContextProvider } from "./context/auth.context";
import { ThemeProvider } from "./providers/theme-provider";
import { TestSequencerWSProvider } from "./context/testSequencerWS.context";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const root = createRoot(document.getElementById("root") as HTMLElement);
const queryClient = new QueryClient();

function fallbackRender({ error, resetErrorBoundary }) {
return <ErrorPage error={error} resetErrorBoundary={resetErrorBoundary} />;
Expand All @@ -26,11 +28,13 @@ root.render(
<HashRouter>
<ErrorBoundary fallbackRender={fallbackRender}>
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<AuthContextProvider>
<TestSequencerWSProvider>
<App />
</TestSequencerWSProvider>
</AuthContextProvider>
<QueryClientProvider client={queryClient}>
<AuthContextProvider>
<TestSequencerWSProvider>
<App />
</TestSequencerWSProvider>
</AuthContextProvider>
</QueryClientProvider>
</ThemeProvider>
</ErrorBoundary>
</HashRouter>,
Expand Down
14 changes: 6 additions & 8 deletions src/renderer/routes/flow_chart/views/env-var/EnvVarModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,23 +100,21 @@ const EnvVarModal = () => {

const handleSetCloudKey = async () => {
if (flojoyCloudKey === "") {
toast("Please enter your Flojoy Cloud API key");
toast("Please enter your Flojoy Cloud workspace secret");
return;
}
const res = await postEnvironmentVariable({
key: "FLOJOY_CLOUD_KEY",
key: "FLOJOY_CLOUD_WORKSPACE_SECRET",
value: flojoyCloudKey,
});
res.match(
() => {
toast(
"Successfully set your Flojoy Cloud API key, let's stream some data to the cloud!",
);
toast("Successfully set your Flojoy Cloud workspace secret!");
setFlojoyCloudKey("");
fetchCredentials();
},
(e) => {
toast("Error adding your Flojoy Cloud API key", {
toast("Error adding your Flojoy Cloud workspace secret", {
description: e.message,
});
},
Expand All @@ -140,7 +138,7 @@ const EnvVarModal = () => {
target="_blank"
className="text-sm underline"
>
Get your Flojoy Cloud API key
Get your Flojoy Cloud workspace secret (in your workspace settings)
</a>
<div className="py-1" />
<div className="flex w-full items-center space-x-2">
Expand All @@ -150,7 +148,7 @@ const EnvVarModal = () => {
onChange={(e) => setFlojoyCloudKey(e.target.value)}
className="bg-modal"
data-testid="flojoy-cloud-api-input"
placeholder="Paste your Flojoy Cloud API key here :)"
placeholder="Paste your Flojoy Cloud workspace secret here :)"
/>
<Button
data-testid="flojoy-cloud-api-submit"
Expand Down
98 changes: 70 additions & 28 deletions src/renderer/routes/test_sequencer_panel/components/CloudPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { Input } from "@/renderer/components/ui/input";
import { useTestSequencerState } from "@/renderer/hooks/useTestSequencerState";
import LockableButton from "./lockable/LockedButtons";
Expand All @@ -14,38 +14,86 @@ import { Button } from "@/renderer/components/ui/button";
import { useAppStore } from "@/renderer/stores/app";
import { useShallow } from "zustand/react/shallow";
import { testSequenceExportCloud } from "@/renderer/routes/test_sequencer_panel/models/models";
import { Project, getCloudProjects } from "@/renderer/lib/api";
import { getCloudProjects, getEnvironmentVariables } from "@/renderer/lib/api";
import { toastQueryError } from "@/renderer/utils/report-error";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Spinner } from "@/renderer/components/ui/spinner";

export function CloudPanel() {
const queryClient = useQueryClient();
const [hardwareId, setHardwareId] = useState("");
const [projectId, setProjectId] = useState("");
const { tree, setIsLocked } = useTestSequencerState();
const { tSSendJsonMessage } = useTestSequencerWS();
const [projects, setProjects] = useState<Project[]>([]);

const { setIsEnvVarModalOpen } = useAppStore(
const envsQuery = useQuery({
queryKey: ["envs"],
queryFn: async () => {
const res = await getEnvironmentVariables();
return res.match(
(vars) => vars,
(e) => {
toastQueryError(e, "Failed to fetch environment variables");
return [];
},
);
},
});

const projectsQuery = useQuery({
queryKey: ["projects"],
queryFn: async () => {
if (envsQuery.isSuccess) {
if (
envsQuery.data.some((c) => c.key === "FLOJOY_CLOUD_WORKSPACE_SECRET")
) {
const res = await getCloudProjects();
return res.match(
(vars) => vars,
(e) => {
toastQueryError(e, "Failed to fetch cloud projects");
return [];
},
);
}
}
return [];
},
enabled: envsQuery.isSuccess,
});

const { isEnvVarModalOpen, setIsEnvVarModalOpen } = useAppStore(
useShallow((state) => ({
isEnvVarModalOpen: state.isEnvVarModalOpen,
setIsEnvVarModalOpen: state.setIsEnvVarModalOpen,
})),
);

useEffect(() => {
queryClient.invalidateQueries({ queryKey: ["envs"] });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEnvVarModalOpen]);

const handleExport = () => {
setIsLocked(true);
tSSendJsonMessage(testSequenceExportCloud(tree, hardwareId, projectId));
setIsLocked(true);
};

const fetchProjects = useCallback(async () => {
(await getCloudProjects()).match(
(p) => setProjects(p),
(e) => toastQueryError(e, "Failed to fetch projects from Flojoy Cloud"),
);
}, []);
if (!envsQuery.isSuccess || !projectsQuery.isSuccess) {
return <Spinner />;
}

useEffect(() => {
fetchProjects();
}, [fetchProjects]);
const isCloudKeySet = envsQuery.data.some(
(c) => c.key === "FLOJOY_CLOUD_WORKSPACE_SECRET",
);

if (!isCloudKeySet) {
return (
<Button onClick={() => setIsEnvVarModalOpen(true)} className="w-full">
Connect to Flojoy Cloud
</Button>
);
}

return (
<div className="min-w-[240px] rounded-xl border border-gray-300 p-4 py-4 dark:border-gray-800">
Expand Down Expand Up @@ -75,24 +123,18 @@ export function CloudPanel() {
<SelectValue placeholder={"Select a project..."} />
</SelectTrigger>
<SelectContent className="max-h-72">
{projects.length === 0 && (
{projectsQuery.data.length === 0 && (
<div className="flex flex-col items-center justify-center gap-2 p-2 text-sm">
<strong>No projects found</strong>
<p>Did you forget to set Flojoy Cloud API key?</p>
<div className="flex items-center justify-center gap-2">
<Button
onClick={() => setIsEnvVarModalOpen(true)}
variant={"ghost"}
>
Set API Key
</Button>
<Button onClick={fetchProjects} variant={"ghost"}>
Refresh project list
</Button>
</div>
<Button
onClick={() => projectsQuery.refetch()}
variant={"ghost"}
>
Refresh project list
</Button>
</div>
)}
{projects.map((option) => (
{projectsQuery.data.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
Expand Down

0 comments on commit 9f5a816

Please sign in to comment.