-
Notifications
You must be signed in to change notification settings - Fork 894
feat: offer to restart workspace when ttl is changed #5391
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
Changes from 1 commit
e3fb8cc
86b695d
9408e24
09d2214
1729f2b
cf0b8fd
0c4d52c
7005930
eb529d6
b19837d
da30f5b
c7376ea
9a267d6
ab2bfbf
60d2bef
5aa4e3f
7e83daa
b05e1b1
bb77e4f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,171 +55,180 @@ export type WorkspaceScheduleEvent = | |
| { type: "APPLY_LATER" } | ||
|
||
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 | ||
} | ||
/** @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 | ||
} | ||
}, | ||
}, | ||
}, | ||
initial: "idle", | ||
on: { | ||
GET_WORKSPACE: "gettingWorkspace", | ||
}, | ||
states: { | ||
idle: { | ||
tags: "loading", | ||
initial: "idle", | ||
on: { | ||
GET_WORKSPACE: "gettingWorkspace", | ||
}, | ||
gettingWorkspace: { | ||
entry: ["clearGetWorkspaceError", "clearContext"], | ||
invoke: { | ||
src: "getWorkspace", | ||
id: "getWorkspace", | ||
onDone: { | ||
target: "gettingPermissions", | ||
actions: ["assignWorkspace"], | ||
}, | ||
onError: { | ||
target: "error", | ||
actions: ["assignGetWorkspaceError"], | ||
}, | ||
states: { | ||
idle: { | ||
tags: "loading", | ||
}, | ||
tags: "loading", | ||
}, | ||
gettingPermissions: { | ||
entry: "clearGetPermissionsError", | ||
invoke: { | ||
src: "checkPermissions", | ||
id: "checkPermissions", | ||
onDone: [ | ||
{ | ||
actions: ["assignPermissions"], | ||
target: "presentForm", | ||
gettingWorkspace: { | ||
entry: ["clearGetWorkspaceError", "clearContext"], | ||
invoke: { | ||
src: "getWorkspace", | ||
id: "getWorkspace", | ||
onDone: { | ||
target: "gettingPermissions", | ||
actions: ["assignWorkspace"], | ||
}, | ||
], | ||
onError: [ | ||
{ | ||
actions: "assignGetPermissionsError", | ||
onError: { | ||
target: "error", | ||
actions: ["assignGetWorkspaceError"], | ||
}, | ||
], | ||
}, | ||
tags: "loading", | ||
}, | ||
}, | ||
presentForm: { | ||
on: { | ||
SUBMIT_SCHEDULE: { target: "submittingSchedule", actions: "assignAutoStopChanged" }, | ||
gettingPermissions: { | ||
entry: "clearGetPermissionsError", | ||
invoke: { | ||
src: "checkPermissions", | ||
id: "checkPermissions", | ||
onDone: [ | ||
{ | ||
actions: ["assignPermissions"], | ||
target: "presentForm", | ||
}, | ||
], | ||
onError: [ | ||
{ | ||
actions: "assignGetPermissionsError", | ||
target: "error", | ||
}, | ||
], | ||
}, | ||
}, | ||
}, | ||
submittingSchedule: { | ||
invoke: { | ||
src: "submitSchedule", | ||
id: "submitSchedule", | ||
onDone: [ | ||
{ | ||
cond: "autoStartChanged", | ||
target: "showingRestartDialog", | ||
presentForm: { | ||
on: { | ||
SUBMIT_SCHEDULE: { | ||
target: "submittingSchedule", | ||
actions: "assignAutoStopChanged", | ||
}, | ||
{ target: "done" } | ||
], | ||
onError: { | ||
target: "presentForm", | ||
actions: ["assignSubmissionError"], | ||
}, | ||
}, | ||
tags: "loading", | ||
}, | ||
showingRestartDialog: { | ||
on: { | ||
RESTART_WORKSPACE: { target: "done", actions: "assignRestartWorkspace" }, | ||
APPLY_LATER: "done" | ||
submittingSchedule: { | ||
invoke: { | ||
src: "submitSchedule", | ||
id: "submitSchedule", | ||
onDone: [ | ||
{ | ||
cond: "autoStartChanged", | ||
presleyp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
target: "showingRestartDialog", | ||
}, | ||
{ target: "done" }, | ||
], | ||
onError: { | ||
target: "presentForm", | ||
actions: ["assignSubmissionError"], | ||
}, | ||
}, | ||
tags: "loading", | ||
}, | ||
}, | ||
error: { | ||
on: { | ||
GET_WORKSPACE: "gettingWorkspace", | ||
showingRestartDialog: { | ||
on: { | ||
RESTART_WORKSPACE: { | ||
target: "done", | ||
actions: "assignRestartWorkspace", | ||
}, | ||
APPLY_LATER: "done", | ||
}, | ||
}, | ||
error: { | ||
on: { | ||
GET_WORKSPACE: "gettingWorkspace", | ||
}, | ||
}, | ||
done: {}, | ||
presleyp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}, | ||
done: {} | ||
}, | ||
}, | ||
{ | ||
guards: { | ||
autoStartChanged: (context) => Boolean(context.autoStopChanged) | ||
}, | ||
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, | ||
}), | ||
assignAutoStopChanged: assign({ | ||
autoStopChanged: (_) => true | ||
}), | ||
assignRestartWorkspace: assign({ | ||
shouldRestartWorkspace: (_) => true | ||
}), | ||
clearGetPermissionsError: assign({ | ||
checkPermissionsError: (_) => undefined, | ||
}), | ||
clearContext: () => { | ||
assign({ workspace: undefined, permissions: undefined }) | ||
{ | ||
guards: { | ||
autoStartChanged: (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, | ||
}), | ||
assignAutoStopChanged: assign({ | ||
autoStopChanged: (_, event) => event.autoStopChanged, | ||
}), | ||
assignRestartWorkspace: assign({ | ||
shouldRestartWorkspace: (_) => true, | ||
}), | ||
clearGetPermissionsError: assign({ | ||
checkPermissionsError: (_) => undefined, | ||
}), | ||
clearContext: () => { | ||
assign({ workspace: undefined, permissions: undefined }) | ||
}, | ||
clearGetWorkspaceError: (context) => { | ||
assign({ ...context, getWorkspaceError: undefined }) | ||
}, | ||
}, | ||
}, | ||
|
||
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.") | ||
} | ||
}, | ||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if this errors? I don't see a catch, so I'm not sure how we'd enter the error state on line 157 of this machine. I would want to avoid prompting users to restart their workspace if no change was actually made. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wasn't sure if it was a problem that I'm not returning the result from both promises here, so I tried hard-coding each api call to return a rejected promise, and it behaved the way I intended: if one api call errors and that part of the schedule is not changed, there's no error. If the error api call is called, you stay on the form and get an error message. If you change the start but not the stop, you don't see the prompt. |
||
} | ||
}, | ||
}, | ||
}, | ||
}, | ||
) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Permissions are loaded in the initial app loading in the auth machine so I don't think we need to load it again. If there are new permissions, I would add them there.
I would do is something like this:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I didn't add the permissions stuff in this PR (the machine just got reformatted), but I'll make a note to look into that later.