Skip to content

refactor: clean up workspace and template settings #9654

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

Merged
merged 4 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 34 additions & 0 deletions site/src/api/queries/templates.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,38 @@
import * as API from "api/api";
import { type Template, type AuthorizationResponse } from "api/typesGenerated";
import { type QueryOptions } from "@tanstack/react-query";

export const templateByNameKey = (orgId: string, name: string) => [
orgId,
"template",
name,
"settings",
];

export const templateByName = (
orgId: string,
name: string,
): QueryOptions<{ template: Template; permissions: AuthorizationResponse }> => {
return {
queryKey: templateByNameKey(orgId, name),
queryFn: async () => {
const template = await API.getTemplateByName(orgId, name);
const permissions = await API.checkAuthorization({
checks: {
canUpdateTemplate: {
object: {
resource_type: "template",
resource_id: template.id,
},
action: "update",
},
},
});

return { template, permissions };
},
};
};

const getTemplatesQueryKey = (orgId: string) => [orgId, "templates"];

Expand Down
20 changes: 20 additions & 0 deletions site/src/api/queries/workspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as API from "api/api";
import { type Workspace } from "api/typesGenerated";
import { type QueryOptions } from "@tanstack/react-query";

export const workspaceByOwnerAndNameKey = (owner: string, name: string) => [
"workspace",
owner,
name,
"settings",
];

export const workspaceByOwnerAndName = (
owner: string,
name: string,
): QueryOptions<Workspace> => {
return {
queryKey: workspaceByOwnerAndNameKey(owner, name),
queryFn: () => API.getWorkspaceByOwnerAndName(owner, name),
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ import { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useNavigate, useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import {
getTemplateQuery,
useTemplateSettingsContext,
} from "../TemplateSettingsLayout";
import { useTemplateSettings } from "../TemplateSettingsLayout";
import { TemplateSettingsPageView } from "./TemplateSettingsPageView";
import { templateByNameKey } from "api/queries/templates";
import { useOrganizationId } from "hooks";

export const TemplateSettingsPage: FC = () => {
const { template: templateName } = useParams() as { template: string };
const navigate = useNavigate();
const { template } = useTemplateSettingsContext();
const orgId = useOrganizationId();
const { template } = useTemplateSettings();
const queryClient = useQueryClient();
const {
mutate: updateTemplate,
Expand All @@ -25,9 +25,9 @@ export const TemplateSettingsPage: FC = () => {
(data: UpdateTemplateMeta) => updateTemplateMeta(template.id, data),
{
onSuccess: async () => {
await queryClient.invalidateQueries({
queryKey: getTemplateQuery(templateName),
});
await queryClient.invalidateQueries(
templateByNameKey(orgId, templateName),
);
displaySuccess("Template updated successfully");
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import { FC } from "react";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { templateACLMachine } from "xServices/template/templateACLXService";
import { useTemplateSettingsContext } from "../TemplateSettingsLayout";
import { useTemplateSettings } from "../TemplateSettingsLayout";
import { TemplatePermissionsPageView } from "./TemplatePermissionsPageView";
import { docs } from "utils/docs";

export const TemplatePermissionsPage: FC<
React.PropsWithChildren<unknown>
> = () => {
const organizationId = useOrganizationId();
const { template, permissions } = useTemplateSettingsContext();
const { template, permissions } = useTemplateSettings();
const { template_rbac: isTemplateRBACEnabled } = useFeatureVisibility();
const [state, send] = useMachine(templateACLMachine, {
context: { templateId: template.id },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
}
},
initialTouched,
enableReinitialize: true,
});

const getFieldHelpers = getFormHelpers<TemplateScheduleFormValues>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMutation } from "@tanstack/react-query";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { updateTemplateMeta } from "api/api";
import { UpdateTemplateMeta } from "api/typesGenerated";
import { useDashboard } from "components/Dashboard/DashboardProvider";
Expand All @@ -7,14 +7,17 @@ import { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useNavigate, useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import { useTemplateSettingsContext } from "../TemplateSettingsLayout";
import { useTemplateSettings } from "../TemplateSettingsLayout";
import { TemplateSchedulePageView } from "./TemplateSchedulePageView";
import { useLocalStorage } from "hooks";
import { useLocalStorage, useOrganizationId } from "hooks";
import { templateByNameKey } from "api/queries/templates";

const TemplateSchedulePage: FC = () => {
const { template: templateName } = useParams() as { template: string };
const navigate = useNavigate();
const { template } = useTemplateSettingsContext();
const queryClient = useQueryClient();
const orgId = useOrganizationId();
const { template } = useTemplateSettings();
const { entitlements, experiments } = useDashboard();
const allowAdvancedScheduling =
entitlements.features["advanced_template_scheduling"].enabled;
Expand All @@ -33,7 +36,10 @@ const TemplateSchedulePage: FC = () => {
} = useMutation(
(data: UpdateTemplateMeta) => updateTemplateMeta(template.id, data),
{
onSuccess: () => {
onSuccess: async () => {
await queryClient.invalidateQueries(
templateByNameKey(orgId, templateName),
);
displaySuccess("Template updated successfully");
// clear browser storage of workspaces impending deletion
clearLocal("dismissedWorkspaceList"); // workspaces page
Expand Down
90 changes: 29 additions & 61 deletions site/src/pages/TemplateSettingsPage/TemplateSettingsLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,91 +7,59 @@ import { pageTitle } from "../../utils/page";
import { Loader } from "components/Loader/Loader";
import { Outlet, useParams } from "react-router-dom";
import { Margins } from "components/Margins/Margins";
import { checkAuthorization, getTemplateByName } from "api/api";
import { useQuery } from "@tanstack/react-query";
import { useOrganizationId } from "hooks/useOrganizationId";
import { templateByName } from "api/queries/templates";
import { type AuthorizationResponse, type Template } from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";

const templatePermissions = (templateId: string) =>
({
canUpdateTemplate: {
object: {
resource_type: "template",
resource_id: templateId,
},
action: "update",
},
}) as const;

const fetchTemplateSettings = async (orgId: string, name: string) => {
const template = await getTemplateByName(orgId, name);
const permissions = await checkAuthorization({
checks: templatePermissions(template.id),
});

return {
template,
permissions,
};
};

export const getTemplateQuery = (name: string) => [
"template",
name,
"settings",
];

const useTemplate = (orgId: string, name: string) => {
return useQuery({
queryKey: getTemplateQuery(name),
queryFn: () => fetchTemplateSettings(orgId, name),
keepPreviousData: true,
});
};

const TemplateSettingsContext = createContext<
Awaited<ReturnType<typeof fetchTemplateSettings>> | undefined
const TemplateSettings = createContext<
{ template: Template; permissions: AuthorizationResponse } | undefined
>(undefined);

export const useTemplateSettingsContext = () => {
const context = useContext(TemplateSettingsContext);

if (!context) {
throw new Error(
"useTemplateSettingsContext must be used within a TemplateSettingsContext.Provider",
);
export function useTemplateSettings() {
const value = useContext(TemplateSettings);
if (!value) {
throw new Error("This hook can only be used from a template settings page");
}

return context;
};
return value;
}

export const TemplateSettingsLayout: FC = () => {
const styles = useStyles();
const orgId = useOrganizationId();
const { template: templateName } = useParams() as { template: string };
const { data: settings } = useTemplate(orgId, templateName);
const { data, error, isLoading, isError } = useQuery(
templateByName(orgId, templateName),
);

if (isLoading) {
return <Loader />;
}

return (
<>
<Helmet>
<title>{pageTitle([templateName, "Settings"])}</title>
</Helmet>

{settings ? (
<TemplateSettingsContext.Provider value={settings}>
<Margins>
<Stack className={styles.wrapper} direction="row" spacing={10}>
<Sidebar template={settings.template} />
<Margins>
<Stack className={styles.wrapper} direction="row" spacing={10}>
{isError ? (
<ErrorAlert error={error} />
) : (
<TemplateSettings.Provider value={data}>
<Sidebar template={data.template} />
<Suspense fallback={<Loader />}>
<main className={styles.content}>
<Outlet />
</main>
</Suspense>
</Stack>
</Margins>
</TemplateSettingsContext.Provider>
) : (
<Loader />
)}
</TemplateSettings.Provider>
)}
</Stack>
</Margins>
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { Helmet } from "react-helmet-async";
import { useNavigate, useParams } from "react-router-dom";
import { templateVariablesMachine } from "xServices/template/templateVariablesXService";
import { pageTitle } from "../../../utils/page";
import { useTemplateSettingsContext } from "../TemplateSettingsLayout";
import { useTemplateSettings } from "../TemplateSettingsLayout";
import { TemplateVariablesPageView } from "./TemplateVariablesPageView";

export const TemplateVariablesPage: FC = () => {
Expand All @@ -20,7 +20,7 @@ export const TemplateVariablesPage: FC = () => {
template: string;
};
const organizationId = useOrganizationId();
const { template } = useTemplateSettingsContext();
const { template } = useTemplateSettings();
const navigate = useNavigate();
const [state, send] = useMachine(templateVariablesMachine, {
context: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getWorkspaceParameters, postWorkspaceBuild } from "api/api";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { useWorkspaceSettingsContext } from "../WorkspaceSettingsLayout";
import { useWorkspaceSettings } from "../WorkspaceSettingsLayout";
import { useMutation, useQuery } from "@tanstack/react-query";
import { Loader } from "components/Loader/Loader";
import {
Expand All @@ -17,7 +17,7 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
import { WorkspaceBuildParameter } from "api/typesGenerated";

const WorkspaceParametersPage = () => {
const { workspace } = useWorkspaceSettingsContext();
const workspace = useWorkspaceSettings();
const parameters = useQuery({
queryKey: ["workspace", workspace.id, "parameters"],
queryFn: () => getWorkspaceParameters(workspace),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ export const WorkspaceScheduleForm: FC<
onSubmit,
validationSchema,
initialTouched,
enableReinitialize: true,
});
const formHelpers = getFormHelpers<WorkspaceScheduleFormValues>(
form,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,21 @@ import {
scheduleChanged,
} from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/schedule";
import { ttlMsToAutostop } from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/ttl";
import { useWorkspaceSettingsContext } from "pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
import { useWorkspaceSettings } from "pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
import { FC } from "react";
import { Helmet } from "react-helmet-async";
import { Navigate, useNavigate, useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import * as TypesGen from "api/typesGenerated";
import { workspaceByOwnerAndNameKey } from "api/queries/workspace";
import { WorkspaceScheduleForm } from "./WorkspaceScheduleForm";
import { workspaceSchedule } from "xServices/workspaceSchedule/workspaceScheduleXService";
import {
formValuesToAutostartRequest,
formValuesToTTLRequest,
} from "./formToRequest";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { useQueryClient } from "@tanstack/react-query";

const getAutostart = (workspace: TypesGen.Workspace) =>
scheduleToAutostart(workspace.autostart_schedule);
Expand All @@ -44,7 +46,8 @@ export const WorkspaceSchedulePage: FC = () => {
const navigate = useNavigate();
const username = params.username.replace("@", "");
const workspaceName = params.workspace;
const { workspace } = useWorkspaceSettingsContext();
const queryClient = useQueryClient();
const workspace = useWorkspaceSettings();
const [scheduleState, scheduleSend] = useMachine(workspaceSchedule, {
context: { workspace },
});
Expand Down Expand Up @@ -97,7 +100,7 @@ export const WorkspaceSchedulePage: FC = () => {
onCancel={() => {
navigate(`/@${username}/${workspaceName}`);
}}
onSubmit={(values) => {
onSubmit={async (values) => {
scheduleSend({
type: "SUBMIT_SCHEDULE",
autostart: formValuesToAutostartRequest(values),
Expand All @@ -111,6 +114,10 @@ export const WorkspaceSchedulePage: FC = () => {
values,
),
});

await queryClient.invalidateQueries(
workspaceByOwnerAndNameKey(params.username, params.workspace),
);
}}
/>
)}
Expand Down
Loading