Skip to content

feat: give users ability to duplicate existing workspaces #10195

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d126314
refactor: clean up CreateWorkspacePage
Parkreiner Oct 9, 2023
881d4a3
fix: remove unnecessary state
Parkreiner Oct 9, 2023
f793fe4
refactor: clean up auth logic more
Parkreiner Oct 10, 2023
2416958
refactor: remove enums from workspace actions
Parkreiner Oct 10, 2023
e97cdd8
fix: export ButtonType type
Parkreiner Oct 10, 2023
0af0352
refactor: remove workspace error enums
Parkreiner Oct 10, 2023
61b26f5
chore: get basic clone functionality done for UI
Parkreiner Oct 10, 2023
f0e120b
fix: clean up Create page view again
Parkreiner Oct 11, 2023
f2380fe
Merge branch 'main' into mes/workspace-clone
Parkreiner Oct 11, 2023
53a9cae
refactor/fix: reconcile merge changes from new PR
Parkreiner Oct 11, 2023
d98a5e0
refactor: centralize workspace duplication logic
Parkreiner Oct 12, 2023
ac86d94
refactor: rename rich params validator for clarity
Parkreiner Oct 12, 2023
e290ece
refactor: clean up getInitialRichParameterValues for clarity
Parkreiner Oct 13, 2023
49b4ad6
refactor: clean up TemplateParameters components
Parkreiner Oct 13, 2023
f6b9285
fix: update imports for validator name change
Parkreiner Oct 13, 2023
ba6e20c
wip: commit progress for creating workspaces
Parkreiner Oct 13, 2023
2a7f9e0
refactor: centralize out workspace parameter fetching logic
Parkreiner Oct 13, 2023
bbe44a1
refactor: centralize workspace parameter mutation logic
Parkreiner Oct 13, 2023
1fb2205
feat: get cloning functionality finished
Parkreiner Oct 13, 2023
a7d1ea9
Merge branch 'main' into mes/workspace-clone
Parkreiner Oct 16, 2023
239bddc
fix: make sure button does not auto-submit connected forms
Parkreiner Oct 16, 2023
8dcbc61
fix: add mocked response for created workspaces
Parkreiner Oct 16, 2023
81f868f
Merge branch 'main' into mes/workspace-clone
Parkreiner Oct 19, 2023
2d51fc9
fix: make it possible to split immutable/mutable params
Parkreiner Oct 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: get cloning functionality finished
  • Loading branch information
Parkreiner committed Oct 13, 2023
commit 1fb22051adf15daf971b260c8eed96b44c60d917
13 changes: 12 additions & 1 deletion site/src/api/queries/workspaceBuilds.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { UseInfiniteQueryOptions } from "react-query";
import { QueryOptions, UseInfiniteQueryOptions } from "react-query";
import * as API from "api/api";
import { WorkspaceBuild, WorkspaceBuildsRequest } from "api/typesGenerated";

export function workspaceBuildParametersKey(workspaceId: string) {
return ["workspaceBuilds", workspaceId, "parameters"] as const;
}

export function workspaceBuildParameters(workspaceBuildId: string) {
return {
queryKey: workspaceBuildParametersKey(workspaceBuildId),
queryFn: () => API.getWorkspaceBuildParameters(workspaceBuildId),
} as const satisfies QueryOptions;
}

export const workspaceBuildByNumber = (
username: string,
workspaceName: string,
Expand Down
43 changes: 15 additions & 28 deletions site/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
TemplateVersionParameter,
Workspace,
WorkspaceBuildParameter,
WorkspaceResourceMetadata,
} from "api/typesGenerated";
import { useMe } from "hooks/useMe";
import { useOrganizationId } from "hooks/useOrganizationId";
Expand Down Expand Up @@ -33,6 +32,7 @@ import { checkAuthorization } from "api/queries/authCheck";
import { CreateWSPermissions, createWorkspaceChecks } from "./permissions";
import { richParameters } from "api/queries/templateVersions";
import { useEffectEvent } from "hooks/hookPolyfills";
import { workspaceBuildParameters } from "api/queries/workspaceBuilds";

export const createWorkspaceModes = ["form", "auto", "duplicate"] as const;
export type CreateWorkspaceMode = (typeof createWorkspaceModes)[number];
Expand Down Expand Up @@ -259,30 +259,19 @@ const getDefaultBuildParameters = (
};

function getDuplicationUrlParams(
templateParams: readonly TemplateVersionParameter[],
workspaceParams: readonly WorkspaceBuildParameter[],
workspace: Workspace,
): URLSearchParams {
// Record type makes sure that every property key added starts with "param."
const buildParams: Record<`param.${string}`, string> = {};

const allMetadata: WorkspaceResourceMetadata[] = [];
for (const resource of workspace.latest_build.resources) {
const metadata = resource.metadata;
if (metadata !== undefined) {
allMetadata.push(...metadata);
}
}
// Record type makes sure that every property key added starts with "param.";
// page is also set up to parse params with this prefix for auto mode
const consolidatedParams: Record<`param.${string}`, string> = {};

for (const param of templateParams) {
/**
* @todo Pretty sure the last piece of the puzzle is figuring out how to
* cross-reference the resources from the build with the template params
*/
buildParams[`param.${param.name}`] = String(param["default_value"]);
for (const p of workspaceParams) {
consolidatedParams[`param.${p.name}`] = p.value;
}

return new URLSearchParams({
...buildParams,
...consolidatedParams,
mode: "duplicate" satisfies CreateWorkspaceMode,
name: workspace.name,
version: workspace.template_active_version_id,
Expand All @@ -297,21 +286,19 @@ function getDuplicationUrlParams(
// Meant to be consumed by components outside of this file
export function useWorkspaceDuplication(workspace: Workspace) {
const navigate = useNavigate();

// Sadly, there isn't a good way to derive this data from the workspace itself
const templateParametersQuery = useQuery(
richParameters(workspace.template_active_version_id),
const buildParametersQuery = useQuery(
workspaceBuildParameters(workspace.latest_build.id),
);

// Not using useEffectEvent for this, even with the slightly more complicated
// dependency array, because useEffect isn't really an intended use case
const duplicateWorkspace = useCallback(() => {
const templateParams = templateParametersQuery.data;
if (templateParams === undefined) {
const buildParams = buildParametersQuery.data;
if (buildParams === undefined) {
return;
}

const newUrlParams = getDuplicationUrlParams(templateParams, workspace);
const newUrlParams = getDuplicationUrlParams(buildParams, workspace);

// Necessary for giving modals/popups time to flush their state changes and
// close the popup before actually navigating. Otherwise, you risk the modal
Expand All @@ -322,11 +309,11 @@ export function useWorkspaceDuplication(workspace: Workspace) {
search: newUrlParams.toString(),
});
});
}, [navigate, workspace, templateParametersQuery.data]);
}, [navigate, workspace, buildParametersQuery.data]);

return {
duplicateWorkspace,
duplicationReady: !templateParametersQuery.isLoading,
duplicationReady: !buildParametersQuery.isLoading,
} as const;
}

Expand Down