mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: offer to restart workspace when ttl is changed (#5391)
* Update xstate machine * Fix autoStopChanged * Add dialog * Restart workspace * Clearing location doesn't work and doesn't seem necessary * Fix test * Fix second test * Format * Lint * Use i18n * Switch to fire and forget restart * Improve error handling * Format * Format * Update site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.test.tsx Co-authored-by: Kira Pilot <kira@coder.com> * Fix name of guard * Make done state final * Format Co-authored-by: Kira Pilot <kira@coder.com>
This commit is contained in:
co-authored by
Kira Pilot
parent
2bbeff53f9
commit
8d9528545a
@@ -12,6 +12,7 @@ import templateSettingsPage from "./templateSettingsPage.json"
|
||||
import templateVersionPage from "./templateVersionPage.json"
|
||||
import loginPage from "./loginPage.json"
|
||||
import workspaceChangeVersionPage from "./workspaceChangeVersionPage.json"
|
||||
import workspaceSchedulePage from "./workspaceSchedulePage.json"
|
||||
import serviceBannerSettings from "./serviceBannerSettings.json"
|
||||
|
||||
export const en = {
|
||||
@@ -29,5 +30,6 @@ export const en = {
|
||||
templateVersionPage,
|
||||
loginPage,
|
||||
workspaceChangeVersionPage,
|
||||
workspaceSchedulePage,
|
||||
serviceBannerSettings,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"forbiddenError": "You don't have permissions to update the schedule for this workspace.",
|
||||
"dialogTitle": "Restart workspace?",
|
||||
"dialogDescription": "Would you like to restart your workspace now to apply your new auto-stop setting, or let it apply after your next workspace start?",
|
||||
"restart": "Restart workspace now",
|
||||
"applyLater": "Apply update later"
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
import {
|
||||
MockUser,
|
||||
MockWorkspace,
|
||||
renderWithAuth,
|
||||
} from "testHelpers/renderHelpers"
|
||||
import userEvent from "@testing-library/user-event"
|
||||
import { screen } from "@testing-library/react"
|
||||
import {
|
||||
formValuesToAutoStartRequest,
|
||||
formValuesToTTLRequest,
|
||||
@@ -8,7 +15,14 @@ import {
|
||||
} from "pages/WorkspaceSchedulePage/schedule"
|
||||
import { AutoStop, ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl"
|
||||
import * as TypesGen from "../../api/typesGenerated"
|
||||
import { WorkspaceScheduleFormValues } from "../../components/WorkspaceScheduleForm/WorkspaceScheduleForm"
|
||||
import {
|
||||
WorkspaceScheduleFormValues,
|
||||
Language as FormLanguage,
|
||||
} from "components/WorkspaceScheduleForm/WorkspaceScheduleForm"
|
||||
import { WorkspaceSchedulePage } from "./WorkspaceSchedulePage"
|
||||
import i18next from "i18next"
|
||||
|
||||
const { t } = i18next
|
||||
|
||||
const validValues: WorkspaceScheduleFormValues = {
|
||||
autoStartEnabled: true,
|
||||
@@ -241,4 +255,44 @@ describe("WorkspaceSchedulePage", () => {
|
||||
expect(ttlMsToAutoStop(ttlMs)).toEqual(autoStop)
|
||||
})
|
||||
})
|
||||
|
||||
describe("autoStop change dialog", () => {
|
||||
it("shows if autoStop is changed", async () => {
|
||||
renderWithAuth(<WorkspaceSchedulePage />, {
|
||||
route: `/@${MockUser.username}/${MockWorkspace.name}/schedule`,
|
||||
path: "/@:username/:workspace/schedule",
|
||||
})
|
||||
const user = userEvent.setup()
|
||||
const autoStopToggle = await screen.findByLabelText(
|
||||
FormLanguage.stopSwitch,
|
||||
)
|
||||
await user.click(autoStopToggle)
|
||||
const submitButton = await screen.findByRole("button", {
|
||||
name: /submit/i,
|
||||
})
|
||||
await user.click(submitButton)
|
||||
const title = t("dialogTitle", { ns: "workspaceSchedulePage" })
|
||||
const dialog = await screen.findByText(title)
|
||||
expect(dialog).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("doesn't show if autoStop is not changed", async () => {
|
||||
renderWithAuth(<WorkspaceSchedulePage />, {
|
||||
route: `/@${MockUser.username}/${MockWorkspace.name}/schedule`,
|
||||
path: "/@:username/:workspace/schedule",
|
||||
})
|
||||
const user = userEvent.setup()
|
||||
const autoStartToggle = await screen.findByLabelText(
|
||||
FormLanguage.startSwitch,
|
||||
)
|
||||
await user.click(autoStartToggle)
|
||||
const submitButton = await screen.findByRole("button", {
|
||||
name: /submit/i,
|
||||
})
|
||||
await user.click(submitButton)
|
||||
const title = t("dialogTitle", { ns: "workspaceSchedulePage" })
|
||||
const dialog = screen.queryByText(title)
|
||||
expect(dialog).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import { useMachine } from "@xstate/react"
|
||||
import { AlertBanner } from "components/AlertBanner/AlertBanner"
|
||||
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"
|
||||
import { Margins } from "components/Margins/Margins"
|
||||
import { scheduleToAutoStart } from "pages/WorkspaceSchedulePage/schedule"
|
||||
import { ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import React, { useEffect } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Navigate, useNavigate, useParams } from "react-router-dom"
|
||||
import { scheduleChanged } from "util/schedule"
|
||||
import * as TypesGen from "../../api/typesGenerated"
|
||||
@@ -15,14 +19,20 @@ import {
|
||||
formValuesToTTLRequest,
|
||||
} from "./formToRequest"
|
||||
|
||||
const Language = {
|
||||
forbiddenError:
|
||||
"You don't have permissions to update the schedule for this workspace.",
|
||||
getWorkspaceError: "Failed to fetch workspace.",
|
||||
checkPermissionsError: "Failed to fetch permissions.",
|
||||
}
|
||||
const getAutoStart = (workspace?: TypesGen.Workspace) =>
|
||||
scheduleToAutoStart(workspace?.autostart_schedule)
|
||||
const getAutoStop = (workspace?: TypesGen.Workspace) =>
|
||||
ttlMsToAutoStop(workspace?.ttl_ms)
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
topMargin: {
|
||||
marginTop: `${theme.spacing(3)}px`,
|
||||
},
|
||||
}))
|
||||
|
||||
export const WorkspaceSchedulePage: React.FC = () => {
|
||||
const { t } = useTranslation("workspaceSchedulePage")
|
||||
const styles = useStyles()
|
||||
const { username: usernameQueryParam, workspace: workspaceQueryParam } =
|
||||
useParams()
|
||||
const navigate = useNavigate()
|
||||
@@ -33,6 +43,7 @@ export const WorkspaceSchedulePage: React.FC = () => {
|
||||
checkPermissionsError,
|
||||
submitScheduleError,
|
||||
getWorkspaceError,
|
||||
getTemplateError,
|
||||
permissions,
|
||||
workspace,
|
||||
} = scheduleState.context
|
||||
@@ -45,52 +56,39 @@ export const WorkspaceSchedulePage: React.FC = () => {
|
||||
scheduleSend({ type: "GET_WORKSPACE", username, workspaceName })
|
||||
}, [username, workspaceName, scheduleSend])
|
||||
|
||||
const getAutoStart = (workspace?: TypesGen.Workspace) =>
|
||||
scheduleToAutoStart(workspace?.autostart_schedule)
|
||||
const getAutoStop = (workspace?: TypesGen.Workspace) =>
|
||||
ttlMsToAutoStop(workspace?.ttl_ms)
|
||||
|
||||
const [autoStart, setAutoStart] = useState(getAutoStart(workspace))
|
||||
const [autoStop, setAutoStop] = useState(getAutoStop(workspace))
|
||||
|
||||
useEffect(() => {
|
||||
setAutoStart(getAutoStart(workspace))
|
||||
setAutoStop(getAutoStop(workspace))
|
||||
}, [workspace])
|
||||
|
||||
if (!username || !workspaceName) {
|
||||
return <Navigate to="/workspaces" />
|
||||
}
|
||||
|
||||
if (
|
||||
scheduleState.matches("idle") ||
|
||||
scheduleState.matches("gettingWorkspace") ||
|
||||
scheduleState.matches("gettingPermissions") ||
|
||||
!workspace
|
||||
) {
|
||||
if (scheduleState.hasTag("loading")) {
|
||||
return <FullScreenLoader />
|
||||
}
|
||||
|
||||
if (scheduleState.matches("error")) {
|
||||
return (
|
||||
<AlertBanner
|
||||
severity="error"
|
||||
error={getWorkspaceError || checkPermissionsError}
|
||||
text={
|
||||
getWorkspaceError
|
||||
? Language.getWorkspaceError
|
||||
: Language.checkPermissionsError
|
||||
}
|
||||
retry={() =>
|
||||
scheduleSend({ type: "GET_WORKSPACE", username, workspaceName })
|
||||
}
|
||||
/>
|
||||
<Margins>
|
||||
<div className={styles.topMargin}>
|
||||
<AlertBanner
|
||||
severity="error"
|
||||
error={
|
||||
getWorkspaceError || checkPermissionsError || getTemplateError
|
||||
}
|
||||
retry={() =>
|
||||
scheduleSend({ type: "GET_WORKSPACE", username, workspaceName })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Margins>
|
||||
)
|
||||
}
|
||||
|
||||
if (!permissions?.updateWorkspace) {
|
||||
return (
|
||||
<AlertBanner severity="error" error={Error(Language.forbiddenError)} />
|
||||
<Margins>
|
||||
<div className={styles.topMargin}>
|
||||
<AlertBanner severity="error" error={Error(t("forbiddenError"))} />
|
||||
</div>
|
||||
</Margins>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -101,7 +99,10 @@ export const WorkspaceSchedulePage: React.FC = () => {
|
||||
return (
|
||||
<WorkspaceScheduleForm
|
||||
submitScheduleError={submitScheduleError}
|
||||
initialValues={{ ...autoStart, ...autoStop }}
|
||||
initialValues={{
|
||||
...getAutoStart(workspace),
|
||||
...getAutoStop(workspace),
|
||||
}}
|
||||
isLoading={scheduleState.tags.has("loading")}
|
||||
onCancel={() => {
|
||||
navigate(`/@${username}/${workspaceName}`)
|
||||
@@ -111,15 +112,34 @@ export const WorkspaceSchedulePage: React.FC = () => {
|
||||
type: "SUBMIT_SCHEDULE",
|
||||
autoStart: formValuesToAutoStartRequest(values),
|
||||
ttl: formValuesToTTLRequest(values),
|
||||
autoStartChanged: scheduleChanged(autoStart, values),
|
||||
autoStopChanged: scheduleChanged(autoStop, values),
|
||||
autoStartChanged: scheduleChanged(getAutoStart(workspace), values),
|
||||
autoStopChanged: scheduleChanged(getAutoStop(workspace), values),
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (scheduleState.matches("submitSuccess")) {
|
||||
if (scheduleState.matches("showingRestartDialog")) {
|
||||
return (
|
||||
<ConfirmDialog
|
||||
open
|
||||
title={t("dialogTitle")}
|
||||
description={t("dialogDescription")}
|
||||
confirmText={t("restart")}
|
||||
cancelText={t("applyLater")}
|
||||
hideCancel={false}
|
||||
onConfirm={() => {
|
||||
scheduleSend("RESTART_WORKSPACE")
|
||||
}}
|
||||
onClose={() => {
|
||||
scheduleSend("APPLY_LATER")
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (scheduleState.matches("done")) {
|
||||
return <Navigate to={`/@${username}/${workspaceName}`} />
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,7 @@ export const handlers = [
|
||||
const permissions = [
|
||||
...Object.keys(permissionsToCheck),
|
||||
"canUpdateTemplate",
|
||||
"updateWorkspace",
|
||||
]
|
||||
const response = permissions.reduce((obj, permission) => {
|
||||
return {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import { assign, createMachine } from "xstate"
|
||||
import * as API from "../../api/api"
|
||||
import * as TypesGen from "../../api/typesGenerated"
|
||||
import { displaySuccess } from "../../components/GlobalSnackbar/utils"
|
||||
|
||||
export const Language = {
|
||||
successMessage: "Successfully updated workspace schedule.",
|
||||
@@ -21,9 +20,13 @@ export interface WorkspaceScheduleContext {
|
||||
* machine is partially influenced by workspaceXService.
|
||||
*/
|
||||
workspace?: TypesGen.Workspace
|
||||
template?: TypesGen.Template
|
||||
getTemplateError?: Error | unknown
|
||||
permissions?: Permissions
|
||||
checkPermissionsError?: Error | unknown
|
||||
submitScheduleError?: Error | unknown
|
||||
autoStopChanged?: boolean
|
||||
shouldRestartWorkspace?: boolean
|
||||
}
|
||||
|
||||
export const checks = {
|
||||
@@ -50,160 +53,229 @@ export type WorkspaceScheduleEvent =
|
||||
ttl: TypesGen.UpdateWorkspaceTTLRequest
|
||||
autoStopChanged: boolean
|
||||
}
|
||||
| { type: "RESTART_WORKSPACE" }
|
||||
| { type: "APPLY_LATER" }
|
||||
|
||||
export const workspaceSchedule = createMachine(
|
||||
{
|
||||
id: "workspaceScheduleState",
|
||||
predictableActionArguments: true,
|
||||
tsTypes: {} as import("./workspaceScheduleXService.typegen").Typegen0,
|
||||
schema: {
|
||||
context: {} as WorkspaceScheduleContext,
|
||||
events: {} as WorkspaceScheduleEvent,
|
||||
services: {} as {
|
||||
getWorkspace: {
|
||||
data: TypesGen.Workspace
|
||||
}
|
||||
},
|
||||
},
|
||||
initial: "idle",
|
||||
on: {
|
||||
GET_WORKSPACE: "gettingWorkspace",
|
||||
},
|
||||
states: {
|
||||
idle: {
|
||||
tags: "loading",
|
||||
},
|
||||
gettingWorkspace: {
|
||||
entry: ["clearGetWorkspaceError", "clearContext"],
|
||||
invoke: {
|
||||
src: "getWorkspace",
|
||||
id: "getWorkspace",
|
||||
onDone: {
|
||||
target: "gettingPermissions",
|
||||
actions: ["assignWorkspace"],
|
||||
},
|
||||
onError: {
|
||||
target: "error",
|
||||
actions: ["assignGetWorkspaceError"],
|
||||
},
|
||||
export const workspaceSchedule =
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QHcD2AnA1rADgQwGMwBlAgC0gFcAbEgFzzrAGIBxAUQBUB9AdQHkASgGliABQCCAYXYBtAAwBdRKBypYASzobUAOxUgAHogCMAJgB0AVgAcAThsAWeXZMA2O2YDsdq1YA0IACeiGbyFl428o4AzFYmMT4xbl5mAL5pgWhYuIQk5FS0xAxMFjB02rpQvBjY+ETMEHpgFhq6AG6omC3lNTn1YArKSCBqmtp6BsYI7tbybmZ+9mYO8u6OgSEIMY6OFo5eC34mdqkLNhlZtblEpBQQNPSMPWAVbdXXA8xg6OgYFjhqIwAGYYAC2ZVefTqeSGBjGWh0+hG02SNmsXhMVhWdkcbkcNm8Xk2oRcFgW7hi8h8djcVkuIGyMNuBQeRRKLzeVTEPzBGlgmj0sEazVaHS6LQKBEwPPQfIFSNgcJGCImyNA0wAtOEYrjsa4vD5bMkEiSEG44uTFosCa41lY3AymTd8vdHsVnpCuVBZfLBbphT8-ugAUC6KC5RYpTLefz-UqlPD1IjJijEMkrBE3G4bO5cfN5GYzGadnsDgt5rmnFZHL4nZ88ndCk9Sjh0HAwLo6AAxcHMYgAVQAQgBZACSPGIUgAEuwACIDgAyckTKuTaqmiEzqTMdOzJixJi82OLwUQNic5MxrjxusS5nr-UbrPdHIssEoACM+d6m2yWE0ugtG0nTdO+X4-n+jzKqo65IpuMx7CYF7ZjYMSJLihpeBsZ4zO4Jj7HahomPINaEo4j7Mq6zYeqUH7flolRQFBtAikBYqgS09GQS+tCyCYwyweM8Fpggjg1hYu5UrqZiOCsjjmGaB5uARtYkfq7jUjYjqZIyDYsm67KetxjHvCxLBBv8gIguC4EMXQ5kwaMcGphq6YpBEB4JFYuqxN4Jhmq4bj7CkiReNEpykSYlEuuZtFcWQqDIO8ghwAw6B0HOGh4NQqBQMwgjsMQnASIIPACCI4jSCugnOcJrlGOeMT7LYyExMhClybESmJMFrU2OFDo+OYOlXE+Bk0W+sCJclVSpbA6WZdluX5RIYhiIuACa3CLhInDsIITmqiJbnbG4Oq1gpaxOHmMRKWY2kWPYObnehOwxDFAxxW+lnoGwXB8EIoiSDIR0ueqjXbFYdgWKcVZmCN5x+EpBJPa4ngmLE8jtV4GS6boqAQHABjOl9vEtmASb1RDWqo75GmGr4aFuGampYpYTgI-IUS+Nzhy47ppPPoZFOtBAtBUymNOmOEUQ1nidJWMe1IPazdIWDsXMHseXgxFEAtjVR32euUTHQi6ksbqJXgWPI8y1rJix23ESso3sdvRLrKxmDEjvpIL+nUf+8VekxvpxoqlsnZD6K5nYLhHnJNjYmsZoEjbuu+ASJqdbrn3C5Nnpth2Xa9nKUcNdMuxPdjaFOFS2ErGakTNQjDu3qkJF2PnE3B1NEGmVU5kV9LMzhNDSvzBa1IqZ4OFbLSMMWj7OZoXrhIfQH41B6+xkzSlaV4BlWU5XlI8ISReyYbJ5h2A4cnI7hI2ZtmcSRNpOZxP7huxeTIe-efUSthMyyU8PHO+R4LRKQks4HMmMDhfzWNFLeRs-5vkApTNc1MEK6hAe4c6pE6QDTCCzJ+tZY4nD1qkdCqQBYZCAA */
|
||||
createMachine(
|
||||
{
|
||||
id: "workspaceScheduleState",
|
||||
predictableActionArguments: true,
|
||||
tsTypes: {} as import("./workspaceScheduleXService.typegen").Typegen0,
|
||||
schema: {
|
||||
context: {} as WorkspaceScheduleContext,
|
||||
events: {} as WorkspaceScheduleEvent,
|
||||
services: {} as {
|
||||
getWorkspace: {
|
||||
data: TypesGen.Workspace
|
||||
}
|
||||
getTemplate: {
|
||||
data: TypesGen.Template
|
||||
}
|
||||
},
|
||||
tags: "loading",
|
||||
},
|
||||
gettingPermissions: {
|
||||
entry: "clearGetPermissionsError",
|
||||
invoke: {
|
||||
src: "checkPermissions",
|
||||
id: "checkPermissions",
|
||||
onDone: [
|
||||
{
|
||||
actions: ["assignPermissions"],
|
||||
target: "presentForm",
|
||||
initial: "idle",
|
||||
on: {
|
||||
GET_WORKSPACE: "gettingWorkspace",
|
||||
},
|
||||
states: {
|
||||
idle: {
|
||||
tags: "loading",
|
||||
},
|
||||
gettingWorkspace: {
|
||||
entry: ["clearGetWorkspaceError", "clearContext"],
|
||||
invoke: {
|
||||
src: "getWorkspace",
|
||||
id: "getWorkspace",
|
||||
onDone: {
|
||||
target: "gettingPermissions",
|
||||
actions: ["assignWorkspace"],
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: "assignGetPermissionsError",
|
||||
onError: {
|
||||
target: "error",
|
||||
actions: ["assignGetWorkspaceError"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
presentForm: {
|
||||
on: {
|
||||
SUBMIT_SCHEDULE: "submittingSchedule",
|
||||
},
|
||||
},
|
||||
submittingSchedule: {
|
||||
invoke: {
|
||||
src: "submitSchedule",
|
||||
id: "submitSchedule",
|
||||
onDone: {
|
||||
target: "submitSuccess",
|
||||
actions: "displaySuccess",
|
||||
},
|
||||
onError: {
|
||||
target: "presentForm",
|
||||
actions: ["assignSubmissionError"],
|
||||
tags: "loading",
|
||||
},
|
||||
gettingPermissions: {
|
||||
entry: "clearGetPermissionsError",
|
||||
invoke: {
|
||||
src: "checkPermissions",
|
||||
id: "checkPermissions",
|
||||
onDone: [
|
||||
{
|
||||
actions: ["assignPermissions"],
|
||||
target: "gettingTemplate",
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: "assignGetPermissionsError",
|
||||
target: "error",
|
||||
},
|
||||
],
|
||||
},
|
||||
tags: "loading",
|
||||
},
|
||||
gettingTemplate: {
|
||||
entry: "clearGetTemplateError",
|
||||
invoke: {
|
||||
src: "getTemplate",
|
||||
id: "getTemplate",
|
||||
onDone: {
|
||||
target: "presentForm",
|
||||
actions: ["assignTemplate"],
|
||||
},
|
||||
onError: {
|
||||
target: "error",
|
||||
actions: ["assignGetTemplateError"],
|
||||
},
|
||||
},
|
||||
tags: "loading",
|
||||
},
|
||||
presentForm: {
|
||||
on: {
|
||||
SUBMIT_SCHEDULE: {
|
||||
target: "submittingSchedule",
|
||||
actions: "assignAutoStopChanged",
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: "loading",
|
||||
},
|
||||
submitSuccess: {
|
||||
on: {
|
||||
SUBMIT_SCHEDULE: "submittingSchedule",
|
||||
submittingSchedule: {
|
||||
invoke: {
|
||||
src: "submitSchedule",
|
||||
id: "submitSchedule",
|
||||
onDone: [
|
||||
{
|
||||
cond: "autoStopChanged",
|
||||
target: "showingRestartDialog",
|
||||
},
|
||||
{ target: "done" },
|
||||
],
|
||||
onError: {
|
||||
target: "presentForm",
|
||||
actions: ["assignSubmissionError"],
|
||||
},
|
||||
},
|
||||
tags: "loading",
|
||||
},
|
||||
},
|
||||
error: {
|
||||
on: {
|
||||
GET_WORKSPACE: "gettingWorkspace",
|
||||
showingRestartDialog: {
|
||||
on: {
|
||||
RESTART_WORKSPACE: {
|
||||
target: "done",
|
||||
actions: "restartWorkspace",
|
||||
},
|
||||
APPLY_LATER: "done",
|
||||
},
|
||||
},
|
||||
error: {
|
||||
on: {
|
||||
GET_WORKSPACE: "gettingWorkspace",
|
||||
},
|
||||
},
|
||||
done: {
|
||||
type: "final",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
actions: {
|
||||
assignSubmissionError: assign({
|
||||
submitScheduleError: (_, event) => event.data,
|
||||
}),
|
||||
assignWorkspace: assign({
|
||||
workspace: (_, event) => event.data,
|
||||
}),
|
||||
assignGetWorkspaceError: assign({
|
||||
getWorkspaceError: (_, event) => event.data,
|
||||
}),
|
||||
assignPermissions: assign({
|
||||
// Setting event.data as Permissions to be more stricted. So we know
|
||||
// what permissions we asked for.
|
||||
permissions: (_, event) => event.data as Permissions,
|
||||
}),
|
||||
assignGetPermissionsError: assign({
|
||||
checkPermissionsError: (_, event) => event.data,
|
||||
}),
|
||||
clearGetPermissionsError: assign({
|
||||
checkPermissionsError: (_) => undefined,
|
||||
}),
|
||||
clearContext: () => {
|
||||
assign({ workspace: undefined, permissions: undefined })
|
||||
{
|
||||
guards: {
|
||||
autoStopChanged: (context) => Boolean(context.autoStopChanged),
|
||||
},
|
||||
clearGetWorkspaceError: (context) => {
|
||||
assign({ ...context, getWorkspaceError: undefined })
|
||||
actions: {
|
||||
assignSubmissionError: assign({
|
||||
submitScheduleError: (_, event) => event.data,
|
||||
}),
|
||||
assignWorkspace: assign({
|
||||
workspace: (_, event) => event.data,
|
||||
}),
|
||||
assignGetWorkspaceError: assign({
|
||||
getWorkspaceError: (_, event) => event.data,
|
||||
}),
|
||||
assignPermissions: assign({
|
||||
// Setting event.data as Permissions to be more stricted. So we know
|
||||
// what permissions we asked for.
|
||||
permissions: (_, event) => event.data as Permissions,
|
||||
}),
|
||||
assignGetPermissionsError: assign({
|
||||
checkPermissionsError: (_, event) => event.data,
|
||||
}),
|
||||
assignTemplate: assign({
|
||||
template: (_, event) => event.data,
|
||||
}),
|
||||
assignGetTemplateError: assign({
|
||||
getTemplateError: (_, event) => event.data,
|
||||
}),
|
||||
clearGetTemplateError: assign({
|
||||
getTemplateError: (_) => undefined,
|
||||
}),
|
||||
assignAutoStopChanged: assign({
|
||||
autoStopChanged: (_, event) => event.autoStopChanged,
|
||||
}),
|
||||
clearGetPermissionsError: assign({
|
||||
checkPermissionsError: (_) => undefined,
|
||||
}),
|
||||
clearContext: () => {
|
||||
assign({ workspace: undefined, permissions: undefined })
|
||||
},
|
||||
clearGetWorkspaceError: (context) => {
|
||||
assign({ ...context, getWorkspaceError: undefined })
|
||||
},
|
||||
// action instead of service because we fire and forget so that the
|
||||
// user can return to the workspace page to see the restart
|
||||
restartWorkspace: (context) => {
|
||||
if (context.workspace && context.template) {
|
||||
return API.startWorkspace(
|
||||
context.workspace.id,
|
||||
context.template.active_version_id,
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
displaySuccess: () => {
|
||||
displaySuccess(Language.successMessage)
|
||||
},
|
||||
},
|
||||
|
||||
services: {
|
||||
getWorkspace: async (_, event) => {
|
||||
return await API.getWorkspaceByOwnerAndName(
|
||||
event.username,
|
||||
event.workspaceName,
|
||||
)
|
||||
},
|
||||
checkPermissions: async (context) => {
|
||||
if (context.workspace) {
|
||||
return await API.checkAuthorization({
|
||||
checks: permissionsToCheck(context.workspace),
|
||||
})
|
||||
} else {
|
||||
throw Error(
|
||||
"Cannot check permissions without both workspace and user id",
|
||||
services: {
|
||||
getWorkspace: async (_, event) => {
|
||||
return await API.getWorkspaceByOwnerAndName(
|
||||
event.username,
|
||||
event.workspaceName,
|
||||
)
|
||||
}
|
||||
},
|
||||
submitSchedule: async (context, event) => {
|
||||
if (!context.workspace?.id) {
|
||||
// This state is theoretically impossible, but helps TS
|
||||
throw new Error("Failed to load workspace.")
|
||||
}
|
||||
},
|
||||
getTemplate: async (context) => {
|
||||
if (context.workspace) {
|
||||
return await API.getTemplate(context.workspace.template_id)
|
||||
} else {
|
||||
throw Error("Can't fetch template without workspace.")
|
||||
}
|
||||
},
|
||||
checkPermissions: async (context) => {
|
||||
if (context.workspace) {
|
||||
return await API.checkAuthorization({
|
||||
checks: permissionsToCheck(context.workspace),
|
||||
})
|
||||
} else {
|
||||
throw Error(
|
||||
"Cannot check permissions without both workspace and user id",
|
||||
)
|
||||
}
|
||||
},
|
||||
submitSchedule: async (context, event) => {
|
||||
if (!context.workspace?.id) {
|
||||
// This state is theoretically impossible, but helps TS
|
||||
throw new Error("Failed to load workspace.")
|
||||
}
|
||||
|
||||
if (event.autoStartChanged) {
|
||||
await API.putWorkspaceAutostart(context.workspace.id, event.autoStart)
|
||||
}
|
||||
if (event.autoStopChanged) {
|
||||
await API.putWorkspaceAutostop(context.workspace.id, event.ttl)
|
||||
}
|
||||
if (event.autoStartChanged) {
|
||||
await API.putWorkspaceAutostart(
|
||||
context.workspace.id,
|
||||
event.autoStart,
|
||||
)
|
||||
}
|
||||
if (event.autoStopChanged) {
|
||||
await API.putWorkspaceAutostop(context.workspace.id, event.ttl)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user