diff --git a/cli/pkg/local/server.go b/cli/pkg/local/server.go index 8b3e8d9b3cd..500f555b13d 100644 --- a/cli/pkg/local/server.go +++ b/cli/pkg/local/server.go @@ -600,8 +600,6 @@ func (s *Server) RedeployProject(ctx context.Context, r *connect.Request[localv1 if err != nil { return nil, err } - } else { - // TODO: should we push change to github for redeploy on github connected project? } // TODO : Add other update project fields diff --git a/web-common/src/features/dashboards/workspace/DeployDashboardCTA.svelte b/web-common/src/features/dashboards/workspace/DeployDashboardCTA.svelte index e8e2c695b42..4a2e4008d34 100644 --- a/web-common/src/features/dashboards/workspace/DeployDashboardCTA.svelte +++ b/web-common/src/features/dashboards/workspace/DeployDashboardCTA.svelte @@ -11,13 +11,10 @@ import DeployIcon from "@rilldata/web-common/components/icons/DeployIcon.svelte"; import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte"; import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte"; - import { createDeployer } from "@rilldata/web-common/features/project/deploy"; - import { waitUntil } from "@rilldata/web-common/lib/waitUtils"; + import { ProjectDeployer } from "@rilldata/web-common/features/project/deploy"; import { behaviourEvent } from "@rilldata/web-common/metrics/initMetrics"; - import { - createLocalServiceDeployValidation, - createLocalServiceRedeploy, - } from "@rilldata/web-common/runtime-client/local-service"; + import { createLocalServiceDeployValidation } from "@rilldata/web-common/runtime-client/local-service"; + import { get } from "svelte/store"; import { Button } from "../../../components/button"; export let type: "primary" | "secondary" = "primary"; @@ -28,41 +25,37 @@ }, }); $: isDeployed = !!$deployValidation.data?.deployedProjectId; + const deployer = new ProjectDeployer(); + const deployerStatus = deployer.getStatus(); + const deploying = deployer.deploying; let open = false; function onShowDeploy() { - if (isDeployed) { - return onDeploy(); + if (deployer.isDeployed) { + return deployer.deploy(); } open = true; void behaviourEvent?.fireDeployIntentEvent(); } - let deploying = false; - const deploy = createDeployer(); - const redeploy = createLocalServiceRedeploy(); - $: isLoading = $deploy.isLoading || $redeploy.isLoading; - async function onDeploy() { - deploying = true; - - await waitUntil(() => !$deployValidation.isLoading); - if (!(await $deploy.mutateAsync($deployValidation.data!))) return; - - deploying = false; + if (!(await deployer.validate())) return; + await deployer.deploy(); open = false; } function handleVisibilityChange() { - if (document.visibilityState !== "visible" || !deploying) return; - void onDeploy(); + if (document.visibilityState !== "visible" || !get(deploying)) return; + void deployer.validate(); } + + $: console.log($deployerStatus); - @@ -90,7 +83,7 @@ diff --git a/web-common/src/features/project/deploy.ts b/web-common/src/features/project/deploy.ts index 71af2ce4238..4079411a0d4 100644 --- a/web-common/src/features/project/deploy.ts +++ b/web-common/src/features/project/deploy.ts @@ -1,57 +1,101 @@ -import type { ConnectError } from "@connectrpc/connect"; -import { createMutation, CreateMutationOptions } from "@rilldata/svelte-query"; +import { ConnectError } from "@connectrpc/connect"; +import { waitUntil } from "@rilldata/web-common/lib/waitUtils"; import { DeployValidationResponse } from "@rilldata/web-common/proto/gen/rill/local/v1/api_pb"; import { - localServiceDeploy, - localServiceRedeploy, + createLocalServiceDeploy, + createLocalServiceDeployValidation, + createLocalServiceRedeploy, } from "@rilldata/web-common/runtime-client/local-service"; +import { derived, get, writable } from "svelte/store"; -export function createDeployer(options?: { - mutation?: CreateMutationOptions< - Awaited>, - ConnectError, - DeployValidationResponse, - unknown - >; -}) { - const { mutation: mutationOptions } = options ?? {}; - return createMutation< - Awaited>, - unknown, - DeployValidationResponse, - unknown - >(deploy, mutationOptions); -} +export class ProjectDeployer { + public readonly validation: ReturnType< + typeof createLocalServiceDeployValidation + > = createLocalServiceDeployValidation({ + query: { + refetchOnWindowFocus: true, + }, + }); + public readonly deploying = writable(false); + + private readonly deployMutation = createLocalServiceDeploy(); + private readonly redeployMutation = createLocalServiceRedeploy(); -async function deploy(deployValidation: DeployValidationResponse) { - if (!deployValidation.isAuthenticated) { - window.open(`${deployValidation.loginUrl}`, "__target"); - return false; + public getStatus() { + return derived( + [this.validation, this.deployMutation, this.redeployMutation], + ([validation, deployMutation, redeployMutation]) => { + if ( + validation.isFetching || + deployMutation.isLoading || + redeployMutation.isLoading + ) { + return { + isLoading: true, + error: undefined, + }; + } + + return { + isLoading: false, + error: + (validation.error as ConnectError)?.message ?? + (deployMutation.error as ConnectError)?.message ?? + (redeployMutation.error as ConnectError)?.message, + }; + }, + ); } - if (deployValidation.isGithubRepo && !deployValidation.isGithubConnected) { - // if the project is a github repo and not connected to github then redirect to grant access - window.open(`${deployValidation.githubGrantAccessUrl}`, "__target"); - return false; + public get isDeployed() { + const validation = get(this.validation).data as DeployValidationResponse; + return !!validation?.deployedProjectId; } - if (deployValidation.deployedProjectId) { - const resp = await localServiceRedeploy({ - projectId: deployValidation.deployedProjectId, - reupload: !deployValidation.isGithubRepo, - }); - if (resp.frontendUrl) { - window.open(resp.frontendUrl, "__target"); + public async validate() { + this.deploying.set(false); + let validation = get(this.validation).data as DeployValidationResponse; + if (validation?.deployedProjectId) { + return true; + } + + await waitUntil(() => !get(this.validation).isFetching); + validation = get(this.validation).data as DeployValidationResponse; + + if (!validation.isAuthenticated) { + window.open(`${validation.loginUrl}`, "__target"); + this.deploying.set(true); + return false; } - } else { - const resp = await localServiceDeploy({ - projectName: deployValidation.localProjectName, - org: deployValidation.rillUserOrgs[0], - upload: !deployValidation.isGithubRepo, - }); - if (resp.frontendUrl) { + + if (validation.isGithubRepo && !validation.isGithubConnected) { + // if the project is a github repo and not connected to github then redirect to grant access + window.open(`${validation.githubGrantAccessUrl}`, "__target"); + this.deploying.set(true); + return false; + } + + return true; + } + + public async deploy() { + // safeguard around deploy + if (!(await this.validate())) return; + + const validation = get(this.validation).data as DeployValidationResponse; + if (validation.deployedProjectId) { + const resp = await get(this.redeployMutation).mutateAsync({ + projectId: validation.deployedProjectId, + reupload: !validation.isGithubRepo, + }); + window.open(resp.frontendUrl, "__target"); + } else { + const resp = await get(this.deployMutation).mutateAsync({ + projectName: validation.localProjectName, + org: validation.rillUserOrgs[0], + upload: !validation.isGithubRepo, + }); window.open(resp.frontendUrl, "__target"); } } - return true; }