From e189dc1f81658f5f096dff1c0e0f42a88beaa28a Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 25 Nov 2025 18:46:13 +0200 Subject: [PATCH] fix: complete Tasks GA promotion (docs, site) (#20927) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Completes the Coder Tasks GA promotion by updating swagger tags and regenerating API documentation and updating the frontend API structure. ## Related Follows #20923 and #20921 which promoted Tasks from Beta/Experimental to GA. --- 🤖 This change was written by Claude Sonnet 4.5 Thinking using [mux](https://github.com/coder/mux) and reviewed by a human 🏂 --- coderd/aitasks.go | 14 +++--- coderd/apidoc/docs.go | 14 +++--- coderd/apidoc/swagger.json | 14 +++--- docs/manifest.json | 8 +-- .../api/{experimental.md => tasks.md} | 2 +- site/src/api/api.ts | 32 ++++++------ .../modules/dashboard/Navbar/NavbarView.tsx | 2 +- .../TaskDeleteDialog.stories.tsx | 4 +- .../TaskDeleteDialog/TaskDeleteDialog.tsx | 2 +- .../TaskFeedbackDialog.stories.tsx | 17 +++---- .../TaskFeedbackDialog/TaskFeedbackDialog.tsx | 2 +- .../tasks/TaskPrompt/TaskPrompt.stories.tsx | 50 ++++++++----------- .../modules/tasks/TaskPrompt/TaskPrompt.tsx | 4 +- .../TasksSidebar/TasksSidebar.stories.tsx | 14 +++--- .../tasks/TasksSidebar/TasksSidebar.tsx | 2 +- site/src/pages/TaskPage/TaskPage.stories.tsx | 48 +++++++++--------- site/src/pages/TaskPage/TaskPage.tsx | 2 +- .../src/pages/TasksPage/TasksPage.stories.tsx | 14 +++--- site/src/pages/TasksPage/TasksPage.tsx | 2 +- 19 files changed, 118 insertions(+), 129 deletions(-) rename docs/reference/api/{experimental.md => tasks.md} (99%) diff --git a/coderd/aitasks.go b/coderd/aitasks.go index 39b543c0f1..2313ee745f 100644 --- a/coderd/aitasks.go +++ b/coderd/aitasks.go @@ -35,7 +35,7 @@ import ( // @Security CoderSessionToken // @Accept json // @Produce json -// @Tags Experimental +// @Tags Tasks // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param request body codersdk.CreateTaskRequest true "Create task request" // @Success 201 {object} codersdk.Task @@ -401,7 +401,7 @@ func deriveTaskCurrentState( // @ID list-ai-tasks // @Security CoderSessionToken // @Produce json -// @Tags Experimental +// @Tags Tasks // @Param q query string false "Search query for filtering tasks. Supports: owner:, organization:, status:" // @Success 200 {object} codersdk.TasksListResponse // @Router /tasks [get] @@ -501,7 +501,7 @@ func (api *API) convertTasks(ctx context.Context, requesterID uuid.UUID, dbTasks // @ID get-ai-task-by-id-or-name // @Security CoderSessionToken // @Produce json -// @Tags Experimental +// @Tags Tasks // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param task path string true "Task ID, or task name" // @Success 200 {object} codersdk.Task @@ -573,7 +573,7 @@ func (api *API) taskGet(rw http.ResponseWriter, r *http.Request) { // @Summary Delete AI task // @ID delete-ai-task // @Security CoderSessionToken -// @Tags Experimental +// @Tags Tasks // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param task path string true "Task ID, or task name" // @Success 202 @@ -642,7 +642,7 @@ func (api *API) taskDelete(rw http.ResponseWriter, r *http.Request) { // @ID update-ai-task-input // @Security CoderSessionToken // @Accept json -// @Tags Experimental +// @Tags Tasks // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param task path string true "Task ID, or task name" // @Param request body codersdk.UpdateTaskInputRequest true "Update task input request" @@ -722,7 +722,7 @@ func (api *API) taskUpdateInput(rw http.ResponseWriter, r *http.Request) { // @ID send-input-to-ai-task // @Security CoderSessionToken // @Accept json -// @Tags Experimental +// @Tags Tasks // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param task path string true "Task ID, or task name" // @Param request body codersdk.TaskSendRequest true "Task input request" @@ -791,7 +791,7 @@ func (api *API) taskSend(rw http.ResponseWriter, r *http.Request) { // @ID get-ai-task-logs // @Security CoderSessionToken // @Produce json -// @Tags Experimental +// @Tags Tasks // @Param user path string true "Username, user ID, or 'me' for the authenticated user" // @Param task path string true "Task ID, or task name" // @Success 200 {object} codersdk.TaskLogsResponse diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index c92c15f159..186b54716d 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -5463,7 +5463,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Experimental" + "Tasks" ], "summary": "List AI tasks", "operationId": "list-ai-tasks", @@ -5499,7 +5499,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Experimental" + "Tasks" ], "summary": "Create a new AI task", "operationId": "create-a-new-ai-task", @@ -5542,7 +5542,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Experimental" + "Tasks" ], "summary": "Get AI task by ID or name", "operationId": "get-ai-task-by-id-or-name", @@ -5578,7 +5578,7 @@ const docTemplate = `{ } ], "tags": [ - "Experimental" + "Tasks" ], "summary": "Delete AI task", "operationId": "delete-ai-task", @@ -5616,7 +5616,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Experimental" + "Tasks" ], "summary": "Update AI task input", "operationId": "update-ai-task-input", @@ -5663,7 +5663,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Experimental" + "Tasks" ], "summary": "Get AI task logs", "operationId": "get-ai-task-logs", @@ -5704,7 +5704,7 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Experimental" + "Tasks" ], "summary": "Send input to AI task", "operationId": "send-input-to-ai-task", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 406426c34c..097aa188f3 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -4819,7 +4819,7 @@ } ], "produces": ["application/json"], - "tags": ["Experimental"], + "tags": ["Tasks"], "summary": "List AI tasks", "operationId": "list-ai-tasks", "parameters": [ @@ -4849,7 +4849,7 @@ ], "consumes": ["application/json"], "produces": ["application/json"], - "tags": ["Experimental"], + "tags": ["Tasks"], "summary": "Create a new AI task", "operationId": "create-a-new-ai-task", "parameters": [ @@ -4888,7 +4888,7 @@ } ], "produces": ["application/json"], - "tags": ["Experimental"], + "tags": ["Tasks"], "summary": "Get AI task by ID or name", "operationId": "get-ai-task-by-id-or-name", "parameters": [ @@ -4922,7 +4922,7 @@ "CoderSessionToken": [] } ], - "tags": ["Experimental"], + "tags": ["Tasks"], "summary": "Delete AI task", "operationId": "delete-ai-task", "parameters": [ @@ -4956,7 +4956,7 @@ } ], "consumes": ["application/json"], - "tags": ["Experimental"], + "tags": ["Tasks"], "summary": "Update AI task input", "operationId": "update-ai-task-input", "parameters": [ @@ -4999,7 +4999,7 @@ } ], "produces": ["application/json"], - "tags": ["Experimental"], + "tags": ["Tasks"], "summary": "Get AI task logs", "operationId": "get-ai-task-logs", "parameters": [ @@ -5036,7 +5036,7 @@ } ], "consumes": ["application/json"], - "tags": ["Experimental"], + "tags": ["Tasks"], "summary": "Send input to AI task", "operationId": "send-input-to-ai-task", "parameters": [ diff --git a/docs/manifest.json b/docs/manifest.json index d4fda8da60..56b583eb28 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1166,10 +1166,6 @@ "title": "Enterprise", "path": "./reference/api/enterprise.md" }, - { - "title": "Experimental", - "path": "./reference/api/experimental.md" - }, { "title": "Files", "path": "./reference/api/files.md" @@ -1214,6 +1210,10 @@ "title": "Schemas", "path": "./reference/api/schemas.md" }, + { + "title": "Tasks", + "path": "./reference/api/tasks.md" + }, { "title": "Templates", "path": "./reference/api/templates.md" diff --git a/docs/reference/api/experimental.md b/docs/reference/api/tasks.md similarity index 99% rename from docs/reference/api/experimental.md rename to docs/reference/api/tasks.md index a03521e229..7a85fccefb 100644 --- a/docs/reference/api/experimental.md +++ b/docs/reference/api/tasks.md @@ -1,4 +1,4 @@ -# Experimental +# Tasks ## List AI tasks diff --git a/site/src/api/api.ts b/site/src/api/api.ts index 19c548d067..27c58cd520 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -2644,22 +2644,6 @@ class ApiMethods { markAllInboxNotificationsAsRead = async () => { await this.axios.put("/api/v2/notifications/inbox/mark-all-as-read"); }; -} - -// Experimental API methods call endpoints under the /api/experimental/ prefix. -// These endpoints are not stable and may change or be removed at any time. -// -// All methods must be defined with arrow function syntax. See the docstring -// above the ApiMethods class for a full explanation. - -export type TaskFeedbackRating = "good" | "okay" | "bad"; - -export type CreateTaskFeedbackRequest = { - rate: TaskFeedbackRating; - comment?: string; -}; -class ExperimentalApiMethods { - constructor(protected readonly axios: AxiosInstance) {} createTask = async ( user: string, @@ -2716,6 +2700,22 @@ class ExperimentalApiMethods { setTimeout(() => res(), 500); }); }; +} + +export type TaskFeedbackRating = "good" | "okay" | "bad"; + +export type CreateTaskFeedbackRequest = { + rate: TaskFeedbackRating; + comment?: string; +}; + +// Experimental API methods call endpoints under the /api/experimental/ prefix. +// These endpoints are not stable and may change or be removed at any time. +// +// All methods must be defined with arrow function syntax. See the docstring +// above the ApiMethods class for a full explanation. +class ExperimentalApiMethods { + constructor(protected readonly axios: AxiosInstance) {} getAIBridgeInterceptions = async (options: SearchParamOptions) => { const url = getURLWithSearchParams( diff --git a/site/src/modules/dashboard/Navbar/NavbarView.tsx b/site/src/modules/dashboard/Navbar/NavbarView.tsx index 26c77dfb8f..6cbb8edfde 100644 --- a/site/src/modules/dashboard/Navbar/NavbarView.tsx +++ b/site/src/modules/dashboard/Navbar/NavbarView.tsx @@ -207,7 +207,7 @@ const TasksNavItem: FC = ({ user }) => { }; const { data: idleCount } = useQuery({ queryKey: ["tasks", filter], - queryFn: () => API.experimental.getTasks(filter), + queryFn: () => API.getTasks(filter), refetchInterval: 1_000 * 60, enabled: canSeeTasks, refetchOnWindowFocus: true, diff --git a/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.stories.tsx b/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.stories.tsx index faf4894ec7..4cd119db5c 100644 --- a/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.stories.tsx +++ b/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.stories.tsx @@ -27,7 +27,7 @@ export const DeleteTaskSuccess: Story = { }, }, beforeEach: () => { - spyOn(API.experimental, "deleteTask").mockResolvedValue(); + spyOn(API, "deleteTask").mockResolvedValue(); }, play: async ({ canvasElement, step }) => { const body = within(canvasElement.ownerDocument.body); @@ -39,7 +39,7 @@ export const DeleteTaskSuccess: Story = { await userEvent.click(confirmButton); await step("Confirm delete", async () => { await waitFor(() => { - expect(API.experimental.deleteTask).toHaveBeenCalledWith( + expect(API.deleteTask).toHaveBeenCalledWith( MockTask.owner_name, MockTask.id, ); diff --git a/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.tsx b/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.tsx index 2e8dc14ce7..4e6f526581 100644 --- a/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.tsx +++ b/site/src/modules/tasks/TaskDeleteDialog/TaskDeleteDialog.tsx @@ -20,7 +20,7 @@ export const TaskDeleteDialog: FC = ({ }) => { const queryClient = new QueryClient(); const deleteTaskMutation = useMutation({ - mutationFn: () => API.experimental.deleteTask(task.owner_name, task.id), + mutationFn: () => API.deleteTask(task.owner_name, task.id), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: ["tasks"] }); }, diff --git a/site/src/modules/tasks/TaskFeedbackDialog/TaskFeedbackDialog.stories.tsx b/site/src/modules/tasks/TaskFeedbackDialog/TaskFeedbackDialog.stories.tsx index d1e8e4ddbb..32a6055e35 100644 --- a/site/src/modules/tasks/TaskFeedbackDialog/TaskFeedbackDialog.stories.tsx +++ b/site/src/modules/tasks/TaskFeedbackDialog/TaskFeedbackDialog.stories.tsx @@ -21,7 +21,7 @@ export const Idle: Story = {}; export const Submitting: Story = { beforeEach: async () => { - spyOn(API.experimental, "createTaskFeedback").mockImplementation(() => { + spyOn(API, "createTaskFeedback").mockImplementation(() => { return new Promise(() => {}); }); }, @@ -53,7 +53,7 @@ export const Success: Story = { }, decorators: [withGlobalSnackbar], beforeEach: async () => { - spyOn(API.experimental, "createTaskFeedback").mockResolvedValue(); + spyOn(API, "createTaskFeedback").mockResolvedValue(); }, play: async ({ canvasElement, step }) => { const body = within(canvasElement.ownerDocument.body); @@ -77,20 +77,17 @@ export const Success: Story = { step("submitted successfully", async () => { await body.findByText("Feedback submitted successfully"); - expect(API.experimental.createTaskFeedback).toHaveBeenCalledWith( - MockTask.id, - { - rate: "regular", - comment: "This is my comment", - }, - ); + expect(API.createTaskFeedback).toHaveBeenCalledWith(MockTask.id, { + rate: "regular", + comment: "This is my comment", + }); }); }, }; export const Failure: Story = { beforeEach: async () => { - spyOn(API.experimental, "createTaskFeedback").mockRejectedValue( + spyOn(API, "createTaskFeedback").mockRejectedValue( mockApiError({ message: "Failed to submit feedback", detail: "Server is down", diff --git a/site/src/modules/tasks/TaskFeedbackDialog/TaskFeedbackDialog.tsx b/site/src/modules/tasks/TaskFeedbackDialog/TaskFeedbackDialog.tsx index ddb05fec43..f8599334fb 100644 --- a/site/src/modules/tasks/TaskFeedbackDialog/TaskFeedbackDialog.tsx +++ b/site/src/modules/tasks/TaskFeedbackDialog/TaskFeedbackDialog.tsx @@ -42,7 +42,7 @@ export const TaskFeedbackDialog: FC = ({ isPending, } = useMutation({ mutationFn: (req: CreateTaskFeedbackRequest) => - API.experimental.createTaskFeedback(taskId, req), + API.createTaskFeedback(taskId, req), onSuccess: () => { displaySuccess("Feedback submitted successfully"); }, diff --git a/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx b/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx index 8187ec7500..bd4a765b9c 100644 --- a/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx +++ b/site/src/modules/tasks/TaskPrompt/TaskPrompt.stories.tsx @@ -114,7 +114,7 @@ export const SubmitDisabledWhenPromptEmpty: Story = { export const Submitting: Story = { decorators: [withGlobalSnackbar], beforeEach: () => { - spyOn(API.experimental, "createTask").mockImplementation( + spyOn(API, "createTask").mockImplementation( () => // Never resolve to keep the component in the submitting state for visual testing. new Promise(() => {}), @@ -153,7 +153,7 @@ export const OnSuccess: Story = { ...MockTemplate, active_version_id: activeVersionId, }); - spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + spyOn(API, "createTask").mockResolvedValue(MockTask); }, play: async ({ canvasElement, step }) => { const canvas = within(canvasElement); @@ -167,14 +167,11 @@ export const OnSuccess: Story = { }); await step("Uses latest template version", () => { - expect(API.experimental.createTask).toHaveBeenCalledWith( - MockUserOwner.id, - { - input: MockNewTaskData.initial_prompt, - template_version_id: `${MockTemplate.active_version_id}-latest`, - template_version_preset_id: undefined, - }, - ); + expect(API.createTask).toHaveBeenCalledWith(MockUserOwner.id, { + input: MockNewTaskData.initial_prompt, + template_version_id: `${MockTemplate.active_version_id}-latest`, + template_version_preset_id: undefined, + }); }); await step("Displays success message", async () => { @@ -232,7 +229,7 @@ export const ChangeTemplate: Story = { } return Promise.resolve([]); }); - spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + spyOn(API, "createTask").mockResolvedValue(MockTask); }, play: async ({ canvasElement, step }) => { const canvas = within(canvasElement); @@ -268,7 +265,7 @@ export const SelectTemplateVersion: Story = { name: "v1.0.0", }, ]); - spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + spyOn(API, "createTask").mockResolvedValue(MockTask); }, play: async ({ canvasElement, step }) => { const canvas = within(canvasElement); @@ -295,14 +292,11 @@ export const SelectTemplateVersion: Story = { }); await step("Uses selected version", () => { - expect(API.experimental.createTask).toHaveBeenCalledWith( - MockUserOwner.id, - { - input: MockNewTaskData.initial_prompt, - template_version_id: "test-template-version-2", - template_version_preset_id: undefined, - }, - ); + expect(API.createTask).toHaveBeenCalledWith(MockUserOwner.id, { + input: MockNewTaskData.initial_prompt, + template_version_id: "test-template-version-2", + template_version_preset_id: undefined, + }); }); await step("Displays success message", async () => { @@ -317,8 +311,8 @@ export const OnError: Story = { decorators: [withGlobalSnackbar], beforeEach: () => { spyOn(API, "getTemplate").mockResolvedValue(MockTemplate); - spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); - spyOn(API.experimental, "createTask").mockRejectedValue( + spyOn(API, "getTasks").mockResolvedValue(MockTasks); + spyOn(API, "createTask").mockRejectedValue( mockApiError({ message: "Failed to create task", detail: "You don't have permission to create tasks.", @@ -344,10 +338,10 @@ export const OnError: Story = { export const AuthenticatedExternalAuth: Story = { beforeEach: () => { - spyOn(API.experimental, "getTasks") + spyOn(API, "getTasks") .mockResolvedValueOnce(MockTasks) .mockResolvedValue([MockNewTaskData, ...MockTasks]); - spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + spyOn(API, "createTask").mockResolvedValue(MockTask); spyOn(API, "getTemplateVersionExternalAuth").mockResolvedValue([ MockTemplateVersionExternalAuthGithubAuthenticated, ]); @@ -370,10 +364,10 @@ export const AuthenticatedExternalAuth: Story = { export const MissingExternalAuth: Story = { beforeEach: () => { - spyOn(API.experimental, "getTasks") + spyOn(API, "getTasks") .mockResolvedValueOnce(MockTasks) .mockResolvedValue([MockNewTaskData, ...MockTasks]); - spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + spyOn(API, "createTask").mockResolvedValue(MockTask); spyOn(API, "getTemplateVersionExternalAuth").mockResolvedValue([ MockTemplateVersionExternalAuthGithub, ]); @@ -396,10 +390,10 @@ export const MissingExternalAuth: Story = { export const ExternalAuthError: Story = { beforeEach: () => { - spyOn(API.experimental, "getTasks") + spyOn(API, "getTasks") .mockResolvedValueOnce(MockTasks) .mockResolvedValue([MockNewTaskData, ...MockTasks]); - spyOn(API.experimental, "createTask").mockResolvedValue(MockTask); + spyOn(API, "createTask").mockResolvedValue(MockTask); spyOn(API, "getTemplateVersionExternalAuth").mockRejectedValue( mockApiError({ message: "Failed to load external auth", diff --git a/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx b/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx index 57a567b403..963afaeb61 100644 --- a/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx +++ b/site/src/modules/tasks/TaskPrompt/TaskPrompt.tsx @@ -193,7 +193,7 @@ const CreateTaskForm: FC = ({ templates, onSuccess }) => { mutationFn: async ({ prompt }: CreateTaskMutationFnProps) => { // Users with updateTemplates permission can select the version to use. if (permissions.updateTemplates) { - return API.experimental.createTask(user.id, { + return API.createTask(user.id, { input: prompt, template_version_id: selectedVersionId, template_version_preset_id: selectedPresetId, @@ -453,7 +453,7 @@ async function createTaskWithLatestTemplateVersion( presetId: string | undefined, ): Promise { const template = await API.getTemplate(templateId); - return API.experimental.createTask(userId, { + return API.createTask(userId, { input, template_version_id: template.active_version_id, template_version_preset_id: presetId, diff --git a/site/src/modules/tasks/TasksSidebar/TasksSidebar.stories.tsx b/site/src/modules/tasks/TasksSidebar/TasksSidebar.stories.tsx index de99f37e5f..e4f3404b05 100644 --- a/site/src/modules/tasks/TasksSidebar/TasksSidebar.stories.tsx +++ b/site/src/modules/tasks/TasksSidebar/TasksSidebar.stories.tsx @@ -49,13 +49,13 @@ type Story = StoryObj; export const Loading: Story = { beforeEach: () => { - spyOn(API.experimental, "getTasks").mockReturnValue(new Promise(() => {})); + spyOn(API, "getTasks").mockReturnValue(new Promise(() => {})); }, }; export const Failed: Story = { beforeEach: () => { - spyOn(API.experimental, "getTasks").mockRejectedValue( + spyOn(API, "getTasks").mockRejectedValue( mockApiError({ message: "Failed to fetch tasks", }), @@ -65,7 +65,7 @@ export const Failed: Story = { export const Loaded: Story = { beforeEach: () => { - spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); + spyOn(API, "getTasks").mockResolvedValue(MockTasks); }, }; @@ -82,13 +82,13 @@ export const DisplayName: Story = { export const Empty: Story = { beforeEach: () => { - spyOn(API.experimental, "getTasks").mockResolvedValue([]); + spyOn(API, "getTasks").mockResolvedValue([]); }, }; export const Closed: Story = { beforeEach: () => { - spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); + spyOn(API, "getTasks").mockResolvedValue(MockTasks); }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); @@ -99,7 +99,7 @@ export const Closed: Story = { export const OpenOptionsMenu: Story = { beforeEach: () => { - spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); + spyOn(API, "getTasks").mockResolvedValue(MockTasks); }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); @@ -112,7 +112,7 @@ export const OpenOptionsMenu: Story = { export const OpenDeleteDialog: Story = { beforeEach: () => { - spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); + spyOn(API, "getTasks").mockResolvedValue(MockTasks); }, play: async ({ canvasElement, step }) => { await step("Open menu", async () => { diff --git a/site/src/modules/tasks/TasksSidebar/TasksSidebar.tsx b/site/src/modules/tasks/TasksSidebar/TasksSidebar.tsx index 793864f262..08ab6c891b 100644 --- a/site/src/modules/tasks/TasksSidebar/TasksSidebar.tsx +++ b/site/src/modules/tasks/TasksSidebar/TasksSidebar.tsx @@ -135,7 +135,7 @@ const TasksSidebarGroup: FC = ({ owner }) => { const filter: TasksFilter = { owner }; const tasksQuery = useQuery({ queryKey: ["tasks", filter], - queryFn: () => API.experimental.getTasks(filter), + queryFn: () => API.getTasks(filter), refetchInterval: 10_000, }); diff --git a/site/src/pages/TaskPage/TaskPage.stories.tsx b/site/src/pages/TaskPage/TaskPage.stories.tsx index 27f0b60e3a..b0e191805c 100644 --- a/site/src/pages/TaskPage/TaskPage.stories.tsx +++ b/site/src/pages/TaskPage/TaskPage.stories.tsx @@ -66,7 +66,7 @@ const meta: Meta = { component: TaskPage, decorators: [withProxyProvider(), withAuthProvider], beforeEach: () => { - spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); + spyOn(API, "getTasks").mockResolvedValue(MockTasks); }, parameters: { layout: "fullscreen", @@ -88,13 +88,11 @@ type Story = StoryObj; export const LoadingTask: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockImplementation( - () => new Promise(() => {}), - ); + spyOn(API, "getTask").mockImplementation(() => new Promise(() => {})); }, play: async () => { await waitFor(() => { - expect(API.experimental.getTask).toHaveBeenCalledWith( + expect(API.getTask).toHaveBeenCalledWith( MockTask.owner_name, MockTask.id, ); @@ -104,7 +102,7 @@ export const LoadingTask: Story = { export const LoadingWorkspace: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockImplementation( () => new Promise(() => {}), ); @@ -113,7 +111,7 @@ export const LoadingWorkspace: Story = { export const LoadingTaskError: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockRejectedValue( + spyOn(API, "getTask").mockRejectedValue( mockApiError({ message: "Failed to load task", detail: "You don't have permission to access this resource.", @@ -124,7 +122,7 @@ export const LoadingTaskError: Story = { export const LoadingWorkspaceError: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockRejectedValue( mockApiError({ message: "Failed to load workspace", @@ -136,7 +134,7 @@ export const LoadingWorkspaceError: Story = { export const WaitingOnBuild: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue( MockStartingWorkspace, ); @@ -145,7 +143,7 @@ export const WaitingOnBuild: Story = { export const FailedBuild: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue( MockFailedWorkspace, ); @@ -154,7 +152,7 @@ export const FailedBuild: Story = { export const TerminatedBuild: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue( MockStoppedWorkspace, ); @@ -163,7 +161,7 @@ export const TerminatedBuild: Story = { export const TerminatedBuildWithStatus: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue({ ...MockStoppedWorkspace, latest_app_status: MockWorkspaceAppStatus, @@ -173,7 +171,7 @@ export const TerminatedBuildWithStatus: Story = { export const DeletedWorkspace: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue( MockDeletedWorkspace, ); @@ -182,7 +180,7 @@ export const DeletedWorkspace: Story = { export const WaitingStartupScripts: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue({ ...MockWorkspace, latest_build: { @@ -336,7 +334,7 @@ export const SidebarAppNotFound: Story = { MockClaudeCodeApp, MockVSCodeApp, ); - spyOn(API.experimental, "getTask").mockResolvedValue({ + spyOn(API, "getTask").mockResolvedValue({ ...task, workspace_app_id: null, }); @@ -350,7 +348,7 @@ export const SidebarAppHealthDisabled: Story = { { ...MockClaudeCodeApp, health: "disabled" }, MockVSCodeApp, ); - spyOn(API.experimental, "getTask").mockResolvedValue(task); + spyOn(API, "getTask").mockResolvedValue(task); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue(workspace); }, }; @@ -361,7 +359,7 @@ export const SidebarAppInitializing: Story = { { ...MockClaudeCodeApp, health: "initializing" }, MockVSCodeApp, ); - spyOn(API.experimental, "getTask").mockResolvedValue(task); + spyOn(API, "getTask").mockResolvedValue(task); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue(workspace); }, }; @@ -372,7 +370,7 @@ export const SidebarAppHealthy: Story = { { ...MockClaudeCodeApp, health: "healthy" }, MockVSCodeApp, ); - spyOn(API.experimental, "getTask").mockResolvedValue(task); + spyOn(API, "getTask").mockResolvedValue(task); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue(workspace); }, }; @@ -383,7 +381,7 @@ export const SidebarAppUnhealthy: Story = { { ...MockClaudeCodeApp, health: "unhealthy" }, MockVSCodeApp, ); - spyOn(API.experimental, "getTask").mockResolvedValue(task); + spyOn(API, "getTask").mockResolvedValue(task); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue(workspace); }, }; @@ -394,7 +392,7 @@ const mainAppHealthStory = (health: WorkspaceApp["health"]) => ({ ...MockVSCodeApp, health, }); - spyOn(API.experimental, "getTask").mockResolvedValue(task); + spyOn(API, "getTask").mockResolvedValue(task); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue(workspace); }, }); @@ -410,7 +408,7 @@ export const Active: Story = { MockClaudeCodeApp, MockVSCodeApp, ); - spyOn(API.experimental, "getTask").mockResolvedValue(task); + spyOn(API, "getTask").mockResolvedValue(task); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue(workspace); }, play: async ({ canvasElement }) => { @@ -433,7 +431,7 @@ export const ActivePreview: Story = { MockClaudeCodeApp, MockVSCodeApp, ); - spyOn(API.experimental, "getTask").mockResolvedValue(task); + spyOn(API, "getTask").mockResolvedValue(task); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue(workspace); }, play: async ({ canvasElement }) => { @@ -446,7 +444,7 @@ export const ActivePreview: Story = { export const WorkspaceStarting: Story = { decorators: [withGlobalSnackbar], beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue( MockStoppedWorkspace, ); @@ -484,7 +482,7 @@ export const WorkspaceStarting: Story = { export const WorkspaceStartFailure: Story = { decorators: [withGlobalSnackbar], beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue( MockStoppedWorkspace, ); @@ -522,7 +520,7 @@ export const WorkspaceStartFailure: Story = { export const WorkspaceStartFailureWithDialog: Story = { beforeEach: () => { - spyOn(API.experimental, "getTask").mockResolvedValue(MockTask); + spyOn(API, "getTask").mockResolvedValue(MockTask); spyOn(API, "getWorkspaceByOwnerAndName").mockResolvedValue( MockStoppedWorkspace, ); diff --git a/site/src/pages/TaskPage/TaskPage.tsx b/site/src/pages/TaskPage/TaskPage.tsx index f57d0e9f17..c255feb778 100644 --- a/site/src/pages/TaskPage/TaskPage.tsx +++ b/site/src/pages/TaskPage/TaskPage.tsx @@ -62,7 +62,7 @@ const TaskPage = () => { }; const { data: task, ...taskQuery } = useQuery({ queryKey: ["tasks", username, taskId], - queryFn: () => API.experimental.getTask(username, taskId), + queryFn: () => API.getTask(username, taskId), refetchInterval: ({ state }) => { return state.error ? false : 5_000; }, diff --git a/site/src/pages/TasksPage/TasksPage.stories.tsx b/site/src/pages/TasksPage/TasksPage.stories.tsx index baff3bd2aa..2e12d96983 100644 --- a/site/src/pages/TasksPage/TasksPage.stories.tsx +++ b/site/src/pages/TasksPage/TasksPage.stories.tsx @@ -83,7 +83,7 @@ export const LoadingTemplatesError: Story = { export const LoadingTasks: Story = { beforeEach: () => { spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]); - spyOn(API.experimental, "getTasks").mockImplementation( + spyOn(API, "getTasks").mockImplementation( () => new Promise(() => 1000 * 60 * 60), ); }, @@ -101,7 +101,7 @@ export const LoadingTasks: Story = { export const LoadingTasksError: Story = { beforeEach: () => { spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]); - spyOn(API.experimental, "getTasks").mockRejectedValue( + spyOn(API, "getTasks").mockRejectedValue( mockApiError({ message: "Failed to load tasks", }), @@ -112,14 +112,14 @@ export const LoadingTasksError: Story = { export const EmptyTasks: Story = { beforeEach: () => { spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]); - spyOn(API.experimental, "getTasks").mockResolvedValue([]); + spyOn(API, "getTasks").mockResolvedValue([]); }, }; export const LoadedTasks: Story = { beforeEach: () => { spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]); - spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); + spyOn(API, "getTasks").mockResolvedValue(MockTasks); }, }; @@ -142,7 +142,7 @@ export const LoadedTasksWaitingForInputTab: Story = { beforeEach: () => { const [firstTask, ...otherTasks] = MockTasks; spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]); - spyOn(API.experimental, "getTasks").mockResolvedValue([ + spyOn(API, "getTasks").mockResolvedValue([ { ...firstTask, current_state: { @@ -173,7 +173,7 @@ export const NonAdmin: Story = { }, beforeEach: () => { spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]); - spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); + spyOn(API, "getTasks").mockResolvedValue(MockTasks); }, play: async ({ canvasElement, step }) => { const canvas = within(canvasElement); @@ -190,7 +190,7 @@ export const NonAdmin: Story = { export const OpenDeleteDialog: Story = { beforeEach: () => { spyOn(API, "getTemplates").mockResolvedValue([MockTemplate]); - spyOn(API.experimental, "getTasks").mockResolvedValue(MockTasks); + spyOn(API, "getTasks").mockResolvedValue(MockTasks); }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); diff --git a/site/src/pages/TasksPage/TasksPage.tsx b/site/src/pages/TasksPage/TasksPage.tsx index 83946aee66..7a19ee601b 100644 --- a/site/src/pages/TasksPage/TasksPage.tsx +++ b/site/src/pages/TasksPage/TasksPage.tsx @@ -40,7 +40,7 @@ const TasksPage: FC = () => { }; const tasksQuery = useQuery({ queryKey: ["tasks", filter], - queryFn: () => API.experimental.getTasks(filter), + queryFn: () => API.getTasks(filter), refetchInterval: 10_000, }); const idleTasks = tasksQuery.data?.filter(