mirror of
https://github.com/coder/coder.git
synced 2025-07-09 11:45:56 +00:00
Summary: This adds the client-side implementation to match the types introduced in #879 and #844 as well as a card in the Workspaces page to present workspace the data. Details: * Added a convenient line break in the example schedule.Weekly * Added missing `json:""` annotations in codersdk/workspaces.go * Installed cronstrue for displaying human-friendly cron strings * Adjusted/Added client-side types to match codersdk/workspaces.go * Added new component WorkspaceSchedule.tsx Next Steps: The WorkspaceSchedule.tsx card only presents data (on purpose). In order to make it PUT/modify data, a few changes will be made: - a form for updating workspace schedule will be created - the form will wrapped in a dialog or modal - the WorkspaceSchedule card will have a way of opening the modal which will likely be generalized up to WorkspaceSection.tsx Impact: This is user-facing This does not fully resolve either #274 or #275 (I may further decompose that work to reflect reality and keep things in small deliverable increments), but adds significant progress towards both.
132 lines
4.8 KiB
Go
132 lines
4.8 KiB
Go
package codersdk
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"golang.org/x/xerrors"
|
|
|
|
"github.com/coder/coder/coderd/database"
|
|
)
|
|
|
|
// Workspace is a per-user deployment of a template. It tracks
|
|
// template versions, and can be updated.
|
|
type Workspace struct {
|
|
ID uuid.UUID `json:"id"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
OwnerID uuid.UUID `json:"owner_id"`
|
|
TemplateID uuid.UUID `json:"template_id"`
|
|
TemplateName string `json:"template_name"`
|
|
LatestBuild WorkspaceBuild `json:"latest_build"`
|
|
Outdated bool `json:"outdated"`
|
|
Name string `json:"name"`
|
|
AutostartSchedule string `json:"autostart_schedule"`
|
|
AutostopSchedule string `json:"autostop_schedule"`
|
|
}
|
|
|
|
// CreateWorkspaceBuildRequest provides options to update the latest workspace build.
|
|
type CreateWorkspaceBuildRequest struct {
|
|
TemplateVersionID uuid.UUID `json:"template_version_id"`
|
|
Transition database.WorkspaceTransition `json:"transition" validate:"oneof=create start stop delete,required"`
|
|
DryRun bool `json:"dry_run"`
|
|
}
|
|
|
|
// Workspace returns a single workspace.
|
|
func (c *Client) Workspace(ctx context.Context, id uuid.UUID) (Workspace, error) {
|
|
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaces/%s", id), nil)
|
|
if err != nil {
|
|
return Workspace{}, err
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusOK {
|
|
return Workspace{}, readBodyAsError(res)
|
|
}
|
|
var workspace Workspace
|
|
return workspace, json.NewDecoder(res.Body).Decode(&workspace)
|
|
}
|
|
|
|
func (c *Client) WorkspaceBuilds(ctx context.Context, workspace uuid.UUID) ([]WorkspaceBuild, error) {
|
|
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaces/%s/builds", workspace), nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusOK {
|
|
return nil, readBodyAsError(res)
|
|
}
|
|
var workspaceBuild []WorkspaceBuild
|
|
return workspaceBuild, json.NewDecoder(res.Body).Decode(&workspaceBuild)
|
|
}
|
|
|
|
// CreateWorkspaceBuild queues a new build to occur for a workspace.
|
|
func (c *Client) CreateWorkspaceBuild(ctx context.Context, workspace uuid.UUID, request CreateWorkspaceBuildRequest) (WorkspaceBuild, error) {
|
|
res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/workspaces/%s/builds", workspace), request)
|
|
if err != nil {
|
|
return WorkspaceBuild{}, err
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusCreated {
|
|
return WorkspaceBuild{}, readBodyAsError(res)
|
|
}
|
|
var workspaceBuild WorkspaceBuild
|
|
return workspaceBuild, json.NewDecoder(res.Body).Decode(&workspaceBuild)
|
|
}
|
|
|
|
func (c *Client) WorkspaceBuildByName(ctx context.Context, workspace uuid.UUID, name string) (WorkspaceBuild, error) {
|
|
res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/workspaces/%s/builds/%s", workspace, name), nil)
|
|
if err != nil {
|
|
return WorkspaceBuild{}, err
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusOK {
|
|
return WorkspaceBuild{}, readBodyAsError(res)
|
|
}
|
|
var workspaceBuild WorkspaceBuild
|
|
return workspaceBuild, json.NewDecoder(res.Body).Decode(&workspaceBuild)
|
|
}
|
|
|
|
// UpdateWorkspaceAutostartRequest is a request to update a workspace's autostart schedule.
|
|
type UpdateWorkspaceAutostartRequest struct {
|
|
Schedule string `json:"schedule"`
|
|
}
|
|
|
|
// UpdateWorkspaceAutostart sets the autostart schedule for workspace by id.
|
|
// If the provided schedule is empty, autostart is disabled for the workspace.
|
|
func (c *Client) UpdateWorkspaceAutostart(ctx context.Context, id uuid.UUID, req UpdateWorkspaceAutostartRequest) error {
|
|
path := fmt.Sprintf("/api/v2/workspaces/%s/autostart", id.String())
|
|
res, err := c.request(ctx, http.MethodPut, path, req)
|
|
if err != nil {
|
|
return xerrors.Errorf("update workspace autostart: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusOK {
|
|
return readBodyAsError(res)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// UpdateWorkspaceAutostopRequest is a request to update a workspace's autostop schedule.
|
|
type UpdateWorkspaceAutostopRequest struct {
|
|
Schedule string `json:"schedule"`
|
|
}
|
|
|
|
// UpdateWorkspaceAutostop sets the autostop schedule for workspace by id.
|
|
// If the provided schedule is empty, autostop is disabled for the workspace.
|
|
func (c *Client) UpdateWorkspaceAutostop(ctx context.Context, id uuid.UUID, req UpdateWorkspaceAutostopRequest) error {
|
|
path := fmt.Sprintf("/api/v2/workspaces/%s/autostop", id.String())
|
|
res, err := c.request(ctx, http.MethodPut, path, req)
|
|
if err != nil {
|
|
return xerrors.Errorf("update workspace autostop: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
if res.StatusCode != http.StatusOK {
|
|
return readBodyAsError(res)
|
|
}
|
|
return nil
|
|
}
|