Skip to content

Commit

Permalink
first draft
Browse files Browse the repository at this point in the history
  • Loading branch information
mustard-mh committed Nov 16, 2023
1 parent de85363 commit 1d19634
Show file tree
Hide file tree
Showing 17 changed files with 2,154 additions and 281 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,34 @@
* See License.AGPL.txt in the project root for license information.
*/

import { GitpodServer, WorkspaceCreationResult } from "@gitpod/gitpod-protocol";
import { useMutation } from "@tanstack/react-query";
import { getGitpodService } from "../../service/service";
import { useState } from "react";
import { StartWorkspaceError } from "../../start/StartPage";
import { workspaceClient } from "../../service/public-api";
import {
CreateAndStartWorkspaceRequest,
CreateAndStartWorkspaceResponse,
} from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
import { PartialMessage } from "@bufbuild/protobuf";
import { ConnectError } from "@connectrpc/connect";

export const useCreateWorkspaceMutation = () => {
const [isStarting, setIsStarting] = useState(false);
const mutation = useMutation<WorkspaceCreationResult, StartWorkspaceError, GitpodServer.CreateWorkspaceOptions>({
const mutation = useMutation<
CreateAndStartWorkspaceResponse,
ConnectError,
PartialMessage<CreateAndStartWorkspaceRequest>
>({
mutationFn: async (options) => {
return await getGitpodService().server.createWorkspace(options);
return await workspaceClient.createAndStartWorkspace(options);
},
onMutate: async (options: GitpodServer.CreateWorkspaceOptions) => {
onMutate: async (options: PartialMessage<CreateAndStartWorkspaceRequest>) => {
setIsStarting(true);
},
onError: (error) => {
setIsStarting(false);
},
onSuccess: (result) => {
if (result && result.createdWorkspaceId) {
if (result.workspace?.id) {
// successfully started a workspace, wait a bit before we allow to start another one
setTimeout(() => {
setIsStarting(false);
Expand All @@ -34,7 +42,7 @@ export const useCreateWorkspaceMutation = () => {
},
});
return {
createWorkspace: (options: GitpodServer.CreateWorkspaceOptions) => {
createWorkspace: (options: PartialMessage<CreateAndStartWorkspaceRequest>) => {
return mutation.mutateAsync(options);
},
// Can we use mutation.isLoading here instead?
Expand Down
48 changes: 48 additions & 0 deletions components/dashboard/src/service/json-rpc-workspace-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@ import { CallOptions, Code, ConnectError, PromiseClient } from "@connectrpc/conn
import { PartialMessage } from "@bufbuild/protobuf";
import { WorkspaceService } from "@gitpod/public-api/lib/gitpod/v1/workspace_connect";
import {
CreateAndStartWorkspaceRequest,
CreateAndStartWorkspaceResponse,
GetWorkspaceRequest,
GetWorkspaceResponse,
StartWorkspaceRequest,
StartWorkspaceResponse,
WatchWorkspaceStatusRequest,
WatchWorkspaceStatusResponse,
} from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
import { converter } from "./public-api";
import { getGitpodService } from "./service";
import { generateAsyncGenerator } from "@gitpod/gitpod-protocol/lib/generate-async-generator";
import { WorkspaceInstance } from "@gitpod/gitpod-protocol";
import { validate as uuidValidate } from "uuid";

export class JsonRpcWorkspaceClient implements PromiseClient<typeof WorkspaceService> {
async getWorkspace(request: PartialMessage<GetWorkspaceRequest>): Promise<GetWorkspaceResponse> {
Expand Down Expand Up @@ -80,4 +85,47 @@ export class JsonRpcWorkspaceClient implements PromiseClient<typeof WorkspaceSer
yield response;
}
}

async createAndStartWorkspace(
request: PartialMessage<CreateAndStartWorkspaceRequest>,
options?: CallOptions | undefined,
) {
if (request.source?.source?.case !== "contextUrl") {
throw new ConnectError("not implemented", Code.Unimplemented);
}
if (!request.organizationId || !uuidValidate(request.organizationId)) {
throw new ConnectError("organizationId is required", Code.InvalidArgument);
}
if (!request.editor) {
throw new ConnectError("editor is required", Code.InvalidArgument);
}
const response = await getGitpodService().server.createWorkspace({
organizationId: request.organizationId,
ignoreRunningWorkspaceOnSameCommit: true,
contextUrl: request.source.source.value,
forceDefaultConfig: request.forceDefaultConfig,
workspaceClass: request.workspaceClass,
ideSettings: {
defaultIde: request.editor.name,
useLatestVersion: request.editor.version === "latest",
},
});
const workspace = await this.getWorkspace({ workspaceId: response.createdWorkspaceId });
const result = new CreateAndStartWorkspaceResponse();
result.workspace = workspace.workspace;
return result;
}

async startWorkspace(request: PartialMessage<StartWorkspaceRequest>, options?: CallOptions | undefined) {
if (!request.workspaceId) {
throw new ConnectError("workspaceId is required", Code.InvalidArgument);
}
await getGitpodService().server.startWorkspace(request.workspaceId, {
forceDefaultImage: request.forceDefaultConfig,
});
const workspace = await this.getWorkspace({ workspaceId: request.workspaceId });
const result = new StartWorkspaceResponse();
result.workspace = workspace.workspace;
return result;
}
}
62 changes: 31 additions & 31 deletions components/dashboard/src/workspaces/CreateWorkspacePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,7 @@
* See License.AGPL.txt in the project root for license information.
*/

import {
AdditionalUserData,
CommitContext,
GitpodServer,
SuggestedRepository,
WithReferrerContext,
} from "@gitpod/gitpod-protocol";
import { AdditionalUserData, CommitContext, SuggestedRepository, WithReferrerContext } from "@gitpod/gitpod-protocol";
import { SelectAccountPayload } from "@gitpod/gitpod-protocol/lib/auth";
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
import { Deferred } from "@gitpod/gitpod-protocol/lib/util/deferred";
Expand Down Expand Up @@ -43,6 +37,8 @@ import { UserContext, useCurrentUser } from "../user-context";
import { SelectAccountModal } from "../user-settings/SelectAccountModal";
import { settingsPathIntegrations } from "../user-settings/settings.routes";
import { WorkspaceEntry } from "./WorkspaceEntry";
import { CreateAndStartWorkspaceRequest } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
import { PartialMessage } from "@bufbuild/protobuf";

export function CreateWorkspacePage() {
const { user, setUser } = useContext(UserContext);
Expand Down Expand Up @@ -174,12 +170,20 @@ export function CreateWorkspacePage() {
const [selectAccountError, setSelectAccountError] = useState<SelectAccountPayload | undefined>(undefined);

const createWorkspace = useCallback(
async (options?: Omit<GitpodServer.CreateWorkspaceOptions, "contextUrl" | "organizationId">) => {
async (options?: Omit<PartialMessage<CreateAndStartWorkspaceRequest>, "contextUrl" | "organizationId">) => {
// add options from search params
const opts = options || {};

// we already have shown running workspaces to the user
opts.ignoreRunningWorkspaceOnSameCommit = true;
if (!contextURL) {
return;
}

const organizationId = currentOrg?.id;
if (!organizationId) {
// We need an organizationId for this group of users
console.error("Skipping createWorkspace");
return;
}

// if user received an INVALID_GITPOD_YML yml for their contextURL they can choose to proceed using default configuration
if (workspaceContext.error?.code === ErrorCodes.INVALID_GITPOD_YML) {
Expand All @@ -189,22 +193,12 @@ export function CreateWorkspacePage() {
if (!opts.workspaceClass) {
opts.workspaceClass = selectedWsClass;
}
if (!opts.ideSettings) {
opts.ideSettings = {
defaultIde: selectedIde,
useLatestVersion: useLatestIde,
if (!opts.editor) {
opts.editor = {
name: selectedIde,
version: useLatestIde ? "latest" : undefined,
};
}
if (!contextURL) {
return;
}

const organizationId = currentOrg?.id;
if (!organizationId) {
// We need an organizationId for this group of users
console.error("Skipping createWorkspace");
return;
}

try {
if (createWorkspaceMutation.isStarting) {
Expand All @@ -213,18 +207,24 @@ export function CreateWorkspacePage() {
}
// we wait at least 5 secs
const timeout = new Promise((resolve) => setTimeout(resolve, 5000));

const result = await createWorkspaceMutation.createWorkspace({
contextUrl: contextURL,
organizationId,
projectId: selectedProjectID,
source: {
source: {
case: "contextUrl",
value: contextURL,
},
},
...opts,
organizationId,
configurationId: selectedProjectID,
});
await storeAutoStartOptions();
await timeout;
if (result.workspaceURL) {
window.location.href = result.workspaceURL;
} else if (result.createdWorkspaceId) {
history.push(`/start/#${result.createdWorkspaceId}`);
if (result.workspace?.status?.workspaceUrl) {
window.location.href = result.workspace.status.workspaceUrl;
} else if (result.workspace!.id) {
history.push(`/start/#${result.workspace!.id}`);
}
} catch (error) {
console.log(error);
Expand Down
6 changes: 5 additions & 1 deletion components/public-api/gitpod/v1/workspace.proto
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ message CreateAndStartWorkspaceRequest {

message CreateAndStartWorkspaceResponse { Workspace workspace = 1; }

message StartWorkspaceRequest { string workspace_id = 1; }
message StartWorkspaceRequest {
string workspace_id = 1;

bool force_default_config = 2;
}

message StartWorkspaceResponse { Workspace workspace = 1; }

Expand Down
Loading

0 comments on commit 1d19634

Please sign in to comment.