From 75ae7000e2bc5e1f996c23193a954184c648eb34 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 28 Nov 2022 22:24:44 +0000 Subject: [PATCH 1/3] Send auto start/stop api calls only when changed --- .../WorkspaceSchedulePage.tsx | 14 +++++++++----- .../workspaceSchedule/workspaceScheduleXService.ts | 10 +++++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 492a56ae47aa0..284c070fe29d7 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -1,7 +1,9 @@ +import map from 'lodash/map' +import some from 'lodash/some' import { useMachine } from "@xstate/react" import { AlertBanner } from "components/AlertBanner/AlertBanner" -import { scheduleToAutoStart } from "pages/WorkspaceSchedulePage/schedule" -import { ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" +import { AutoStart, scheduleToAutoStart } from "pages/WorkspaceSchedulePage/schedule" +import { AutoStop, ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" import React, { useEffect, useState } from "react" import { Navigate, useNavigate, useParams } from "react-router-dom" import * as TypesGen from "../../api/typesGenerated" @@ -106,12 +108,14 @@ export const WorkspaceSchedulePage: React.FC = () => { navigate(`/@${username}/${workspaceName}`) }} onSubmit={(values) => { + const autoStartChanged = some(map({ ...autoStart }, (v: boolean|string, k: keyof AutoStart) => values[k] !== v)) + const autoStopChanged = some(map({ ...autoStop }, (v: boolean|string, k: keyof AutoStop) => values[k] !== v)) scheduleSend({ type: "SUBMIT_SCHEDULE", - autoStart: values.autoStartEnabled - ? formValuesToAutoStartRequest(values) - : undefined, + autoStart: formValuesToAutoStartRequest(values), ttl: formValuesToTTLRequest(values), + autoStartChanged, + autoStopChanged }) }} /> diff --git a/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts b/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts index 3d2e7cacfce67..33382097dd535 100644 --- a/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts +++ b/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts @@ -45,8 +45,10 @@ export type WorkspaceScheduleEvent = | { type: "GET_WORKSPACE"; username: string; workspaceName: string } | { type: "SUBMIT_SCHEDULE" - autoStart: TypesGen.UpdateWorkspaceAutostartRequest | undefined + autoStart: TypesGen.UpdateWorkspaceAutostartRequest + autoStartChanged: boolean ttl: TypesGen.UpdateWorkspaceTTLRequest + autoStopChanged: boolean } export const workspaceSchedule = createMachine( @@ -195,10 +197,12 @@ export const workspaceSchedule = createMachine( throw new Error("Failed to load workspace.") } - if (event.autoStart?.schedule !== undefined) { + if (event.autoStartChanged) { await API.putWorkspaceAutostart(context.workspace.id, event.autoStart) } - await API.putWorkspaceAutostop(context.workspace.id, event.ttl) + if (event.autoStopChanged) { + await API.putWorkspaceAutostop(context.workspace.id, event.ttl) + } }, }, }, From aaf0cb343f0f8f13a01e44a85350f3f5d92d37cf Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Mon, 28 Nov 2022 22:32:00 +0000 Subject: [PATCH 2/3] Format --- .../WorkspaceSchedulePage.tsx | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 284c070fe29d7..c21d9e5a0582e 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -1,8 +1,11 @@ -import map from 'lodash/map' -import some from 'lodash/some' +import map from "lodash/map" +import some from "lodash/some" import { useMachine } from "@xstate/react" import { AlertBanner } from "components/AlertBanner/AlertBanner" -import { AutoStart, scheduleToAutoStart } from "pages/WorkspaceSchedulePage/schedule" +import { + AutoStart, + scheduleToAutoStart, +} from "pages/WorkspaceSchedulePage/schedule" import { AutoStop, ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" import React, { useEffect, useState } from "react" import { Navigate, useNavigate, useParams } from "react-router-dom" @@ -108,14 +111,24 @@ export const WorkspaceSchedulePage: React.FC = () => { navigate(`/@${username}/${workspaceName}`) }} onSubmit={(values) => { - const autoStartChanged = some(map({ ...autoStart }, (v: boolean|string, k: keyof AutoStart) => values[k] !== v)) - const autoStopChanged = some(map({ ...autoStop }, (v: boolean|string, k: keyof AutoStop) => values[k] !== v)) + const autoStartChanged = some( + map( + { ...autoStart }, + (v: boolean | string, k: keyof AutoStart) => values[k] !== v, + ), + ) + const autoStopChanged = some( + map( + { ...autoStop }, + (v: boolean | string, k: keyof AutoStop) => values[k] !== v, + ), + ) scheduleSend({ type: "SUBMIT_SCHEDULE", autoStart: formValuesToAutoStartRequest(values), ttl: formValuesToTTLRequest(values), autoStartChanged, - autoStopChanged + autoStopChanged, }) }} /> From a01aae4701a021a09663e7a0de37d0c41a78ae41 Mon Sep 17 00:00:00 2001 From: Presley Pizzo Date: Tue, 29 Nov 2022 16:49:51 +0000 Subject: [PATCH 3/3] Extract and test util function --- .../WorkspaceSchedulePage.tsx | 26 ++----- site/src/util/schedule.test.ts | 71 +++++++++++++++++++ site/src/util/schedule.ts | 17 +++++ 3 files changed, 93 insertions(+), 21 deletions(-) diff --git a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index c21d9e5a0582e..ad48166dea9c7 100644 --- a/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -1,14 +1,10 @@ -import map from "lodash/map" -import some from "lodash/some" import { useMachine } from "@xstate/react" import { AlertBanner } from "components/AlertBanner/AlertBanner" -import { - AutoStart, - scheduleToAutoStart, -} from "pages/WorkspaceSchedulePage/schedule" -import { AutoStop, ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" +import { scheduleToAutoStart } from "pages/WorkspaceSchedulePage/schedule" +import { ttlMsToAutoStop } from "pages/WorkspaceSchedulePage/ttl" import React, { useEffect, useState } from "react" import { Navigate, useNavigate, useParams } from "react-router-dom" +import { scheduleChanged } from "util/schedule" import * as TypesGen from "../../api/typesGenerated" import { FullScreenLoader } from "../../components/Loader/FullScreenLoader" import { WorkspaceScheduleForm } from "../../components/WorkspaceScheduleForm/WorkspaceScheduleForm" @@ -111,24 +107,12 @@ export const WorkspaceSchedulePage: React.FC = () => { navigate(`/@${username}/${workspaceName}`) }} onSubmit={(values) => { - const autoStartChanged = some( - map( - { ...autoStart }, - (v: boolean | string, k: keyof AutoStart) => values[k] !== v, - ), - ) - const autoStopChanged = some( - map( - { ...autoStop }, - (v: boolean | string, k: keyof AutoStop) => values[k] !== v, - ), - ) scheduleSend({ type: "SUBMIT_SCHEDULE", autoStart: formValuesToAutoStartRequest(values), ttl: formValuesToTTLRequest(values), - autoStartChanged, - autoStopChanged, + autoStartChanged: scheduleChanged(autoStart, values), + autoStopChanged: scheduleChanged(autoStop, values), }) }} /> diff --git a/site/src/util/schedule.test.ts b/site/src/util/schedule.test.ts index 452727624a4e6..43b0306203490 100644 --- a/site/src/util/schedule.test.ts +++ b/site/src/util/schedule.test.ts @@ -1,5 +1,7 @@ import dayjs from "dayjs" import duration from "dayjs/plugin/duration" +import { emptySchedule } from "pages/WorkspaceSchedulePage/schedule" +import { emptyTTL } from "pages/WorkspaceSchedulePage/ttl" import { Template, Workspace } from "../api/typesGenerated" import * as Mocks from "../testHelpers/entities" import { @@ -12,6 +14,7 @@ import { getMaxDeadlineChange, getMinDeadline, stripTimezone, + scheduleChanged, } from "./schedule" dayjs.extend(duration) @@ -141,3 +144,71 @@ describe("getMaxDeadlineChange", () => { expect(getMaxDeadlineChange(deadline, minDeadline)).toEqual(2) }) }) + +describe("scheduleChanged", () => { + describe("autoStart", () => { + it("should be true if toggle values are different", () => { + const autoStart = { autoStartEnabled: true, ...emptySchedule } + const formValues = { + autoStartEnabled: false, + ...emptySchedule, + autoStopEnabled: false, + ttl: emptyTTL, + } + expect(scheduleChanged(autoStart, formValues)).toBe(true) + }) + it("should be true if schedule values are different", () => { + const autoStart = { autoStartEnabled: true, ...emptySchedule } + const formValues = { + autoStartEnabled: true, + ...{ ...emptySchedule, monday: true, startTime: "09:00" }, + autoStopEnabled: false, + ttl: emptyTTL, + } + expect(scheduleChanged(autoStart, formValues)).toBe(true) + }) + it("should be false if all autostart values are the same", () => { + const autoStart = { autoStartEnabled: true, ...emptySchedule } + const formValues = { + autoStartEnabled: true, + ...emptySchedule, + autoStopEnabled: false, + ttl: emptyTTL, + } + expect(scheduleChanged(autoStart, formValues)).toBe(false) + }) + }) + + describe("autoStop", () => { + it("should be true if toggle values are different", () => { + const autoStop = { autoStopEnabled: true, ttl: 1000 } + const formValues = { + autoStartEnabled: false, + ...emptySchedule, + autoStopEnabled: false, + ttl: 1000, + } + expect(scheduleChanged(autoStop, formValues)).toBe(true) + }) + it("should be true if ttl values are different", () => { + const autoStop = { autoStopEnabled: true, ttl: 1000 } + const formValues = { + autoStartEnabled: false, + ...emptySchedule, + autoStopEnabled: true, + ttl: 2000, + } + expect(scheduleChanged(autoStop, formValues)).toBe(true) + }) + it("should be false if all autostop values are the same", () => { + const autoStop = { autoStopEnabled: true, ttl: 1000 } + const formValues = { + autoStartEnabled: false, + ...emptySchedule, + autoStopEnabled: true, + ttl: 1000, + } + expect(scheduleChanged(autoStop, formValues)).toBe(false) + }) + }) +}) diff --git a/site/src/util/schedule.ts b/site/src/util/schedule.ts index 4070f8101e388..cd325f2006f4c 100644 --- a/site/src/util/schedule.ts +++ b/site/src/util/schedule.ts @@ -1,3 +1,5 @@ +import map from "lodash/map" +import some from "lodash/some" import cronstrue from "cronstrue" import dayjs, { Dayjs } from "dayjs" import advancedFormat from "dayjs/plugin/advancedFormat" @@ -7,6 +9,9 @@ import timezone from "dayjs/plugin/timezone" import utc from "dayjs/plugin/utc" import { Template, Workspace } from "../api/typesGenerated" import { isWorkspaceOn } from "./workspace" +import { WorkspaceScheduleFormValues } from "components/WorkspaceScheduleForm/WorkspaceScheduleForm" +import { AutoStop } from "pages/WorkspaceSchedulePage/ttl" +import { AutoStart } from "pages/WorkspaceSchedulePage/schedule" // REMARK: some plugins depend on utc, so it's listed first. Otherwise they're // sorted alphabetically. @@ -179,3 +184,15 @@ export const getMaxDeadlineChange = ( deadline: dayjs.Dayjs, extremeDeadline: dayjs.Dayjs, ): number => Math.abs(deadline.diff(extremeDeadline, "hours")) + +export const scheduleChanged = ( + initialValues: AutoStart | AutoStop, + formValues: WorkspaceScheduleFormValues, +): boolean => + some( + map( + { ...initialValues }, + (v: boolean | string, k: keyof typeof initialValues) => + formValues[k] !== v, + ), + )