Skip to content
Merged
8 changes: 4 additions & 4 deletions codersdk/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ type Organization struct {
// CreateTemplateVersionRequest enables callers to create a new Template Version.
type CreateTemplateVersionRequest struct {
// TemplateID optionally associates a version with a template.
TemplateID uuid.UUID `json:"template_id"`
TemplateID uuid.UUID `json:"template_id,omitempty"`

StorageMethod database.ProvisionerStorageMethod `json:"storage_method" validate:"oneof=file,required"`
StorageSource string `json:"storage_source" validate:"required"`
Provisioner database.ProvisionerType `json:"provisioner" validate:"oneof=terraform echo,required"`
// ParameterValues allows for additional parameters to be provided
// during the dry-run provision stage.
ParameterValues []CreateParameterRequest `json:"parameter_values"`
ParameterValues []CreateParameterRequest `json:"parameter_values,omitempty"`
}

// CreateTemplateRequest provides options when creating a template.
Expand All @@ -45,7 +45,7 @@ type CreateTemplateRequest struct {
// template works. There is no reason the data-model cannot support
// empty templates, but it doesn't make sense for users.
VersionID uuid.UUID `json:"template_version_id" validate:"required"`
ParameterValues []CreateParameterRequest `json:"parameter_values"`
ParameterValues []CreateParameterRequest `json:"parameter_values,omitempty"`
}

// CreateWorkspaceRequest provides options for creating a new workspace.
Expand All @@ -54,7 +54,7 @@ type CreateWorkspaceRequest struct {
Name string `json:"name" validate:"username,required"`
// ParameterValues allows for additional parameters to be provided
// during the initial provision.
ParameterValues []CreateParameterRequest `json:"parameter_values"`
ParameterValues []CreateParameterRequest `json:"parameter_values,omitempty"`
}

func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization, error) {
Expand Down
4 changes: 2 additions & 2 deletions codersdk/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ const (
)

type UsersRequest struct {
Search string `json:"search"`
Search string `json:"search,omitempty"`
// Filter users by status
Status string `json:"status"`
Status string `json:"status,omitempty"`
Pagination
}

Expand Down
4 changes: 2 additions & 2 deletions codersdk/workspaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ type Workspace struct {

// CreateWorkspaceBuildRequest provides options to update the latest workspace build.
type CreateWorkspaceBuildRequest struct {
TemplateVersionID uuid.UUID `json:"template_version_id"`
TemplateVersionID uuid.UUID `json:"template_version_id,omitempty"`
Transition database.WorkspaceTransition `json:"transition" validate:"oneof=create start stop delete,required"`
DryRun bool `json:"dry_run"`
DryRun bool `json:"dry_run,omitempty"`
ProvisionerState []byte `json:"state,omitempty"`
}

Expand Down
3 changes: 1 addition & 2 deletions scripts/apitypings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This main.go generates typescript types from the codersdk types in Go.
# Features

- Supports Go types
- [x] Basics (string/int/etc)
- [x] Basics (string/int/etc)
- [x] Maps
- [x] Slices
- [x] Enums
Expand Down Expand Up @@ -36,5 +36,4 @@ type InternalType struct {

# Future Ideas

- Should `omitempty` in the `json` tag indicate optional?
- Use a yaml config for overriding certain types
6 changes: 3 additions & 3 deletions site/src/api/api.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import axios from "axios"
import { getApiKey, login, logout } from "./api"
import { APIKeyResponse, LoginResponse } from "./types"
import * as TypesGen from "./typesGenerated"

// Mock the axios module so that no real network requests are made, but rather
// we swap in a resolved or rejected value
Expand All @@ -12,7 +12,7 @@ describe("api.ts", () => {
describe("login", () => {
it("should return LoginResponse", async () => {
// given
const loginResponse: LoginResponse = {
const loginResponse: TypesGen.LoginWithPasswordResponse = {
session_token: "abc_123_test",
}
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
Expand Down Expand Up @@ -87,7 +87,7 @@ describe("api.ts", () => {
describe("getApiKey", () => {
it("should return APIKeyResponse", async () => {
// given
const apiKeyResponse: APIKeyResponse = {
const apiKeyResponse: TypesGen.GenerateAPIKeyResponse = {
key: "abc_123_test",
}
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
Expand Down
65 changes: 38 additions & 27 deletions site/src/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
import axios, { AxiosRequestHeaders } from "axios"
import { mutate } from "swr"
import * as Types from "./types"
import * as TypesGen from "./typesGenerated"

const CONTENT_TYPE_JSON: AxiosRequestHeaders = {
"Content-Type": "application/json",
}

export const provisioners: Types.Provisioner[] = [
export const provisioners: TypesGen.ProvisionerDaemon[] = [
{
id: "terraform",
name: "Terraform",
created_at: "",
provisioners: [],
},
{
id: "cdr-basic",
name: "Basic",
created_at: "",
provisioners: [],
},
]

export namespace Workspace {
export const create = async (request: Types.CreateWorkspaceRequest): Promise<Types.Workspace> => {
const response = await fetch(`/api/v2/organizations/${request.organization_id}/workspaces`, {
export const create = async (
organizationId: string,
request: TypesGen.CreateWorkspaceRequest,
): Promise<TypesGen.Workspace> => {
const response = await fetch(`/api/v2/organizations/${organizationId}/workspaces`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Expand All @@ -43,13 +49,13 @@ export namespace Workspace {
}
}

export const login = async (email: string, password: string): Promise<Types.LoginResponse> => {
export const login = async (email: string, password: string): Promise<TypesGen.LoginWithPasswordResponse> => {
const payload = JSON.stringify({
email,
password,
})

const response = await axios.post<Types.LoginResponse>("/api/v2/users/login", payload, {
const response = await axios.post<TypesGen.LoginWithPasswordResponse>("/api/v2/users/login", payload, {
headers: { ...CONTENT_TYPE_JSON },
})

Expand All @@ -60,8 +66,8 @@ export const logout = async (): Promise<void> => {
await axios.post("/api/v2/users/logout")
}

export const getUser = async (): Promise<Types.UserResponse> => {
const response = await axios.get<Types.UserResponse>("/api/v2/users/me")
export const getUser = async (): Promise<TypesGen.User> => {
const response = await axios.get<TypesGen.User>("/api/v2/users/me")
return response.data
}

Expand All @@ -70,8 +76,8 @@ export const getAuthMethods = async (): Promise<TypesGen.AuthMethods> => {
return response.data
}

export const getApiKey = async (): Promise<Types.APIKeyResponse> => {
const response = await axios.post<Types.APIKeyResponse>("/api/v2/users/me/keys")
export const getApiKey = async (): Promise<TypesGen.GenerateAPIKeyResponse> => {
const response = await axios.post<TypesGen.GenerateAPIKeyResponse>("/api/v2/users/me/keys")
return response.data
}

Expand All @@ -80,55 +86,57 @@ export const getUsers = async (): Promise<TypesGen.User[]> => {
return response.data
}

export const getOrganization = async (organizationId: string): Promise<Types.Organization> => {
const response = await axios.get<Types.Organization>(`/api/v2/organizations/${organizationId}`)
export const getOrganization = async (organizationId: string): Promise<TypesGen.Organization> => {
const response = await axios.get<TypesGen.Organization>(`/api/v2/organizations/${organizationId}`)
return response.data
}

export const getOrganizations = async (): Promise<Types.Organization[]> => {
const response = await axios.get<Types.Organization[]>("/api/v2/users/me/organizations")
export const getOrganizations = async (): Promise<TypesGen.Organization[]> => {
const response = await axios.get<TypesGen.Organization[]>("/api/v2/users/me/organizations")
return response.data
}

export const getTemplate = async (templateId: string): Promise<Types.Template> => {
const response = await axios.get<Types.Template>(`/api/v2/templates/${templateId}`)
export const getTemplate = async (templateId: string): Promise<TypesGen.Template> => {
const response = await axios.get<TypesGen.Template>(`/api/v2/templates/${templateId}`)
return response.data
}

export const getWorkspace = async (workspaceId: string): Promise<Types.Workspace> => {
const response = await axios.get<Types.Workspace>(`/api/v2/workspaces/${workspaceId}`)
export const getWorkspace = async (workspaceId: string): Promise<TypesGen.Workspace> => {
const response = await axios.get<TypesGen.Workspace>(`/api/v2/workspaces/${workspaceId}`)
return response.data
}

export const getWorkspaceByOwnerAndName = async (
organizationID: string,
username = "me",
workspaceName: string,
): Promise<Types.Workspace> => {
const response = await axios.get<Types.Workspace>(
): Promise<TypesGen.Workspace> => {
const response = await axios.get<TypesGen.Workspace>(
`/api/v2/organizations/${organizationID}/workspaces/${username}/${workspaceName}`,
)
return response.data
}

export const getWorkspaceResources = async (workspaceBuildID: string): Promise<Types.WorkspaceResource[]> => {
const response = await axios.get<Types.WorkspaceResource[]>(`/api/v2/workspacebuilds/${workspaceBuildID}/resources`)
export const getWorkspaceResources = async (workspaceBuildID: string): Promise<TypesGen.WorkspaceResource[]> => {
const response = await axios.get<TypesGen.WorkspaceResource[]>(
`/api/v2/workspacebuilds/${workspaceBuildID}/resources`,
)
return response.data
}

export const createUser = async (user: Types.CreateUserRequest): Promise<TypesGen.User> => {
export const createUser = async (user: TypesGen.CreateUserRequest): Promise<TypesGen.User> => {
const response = await axios.post<TypesGen.User>("/api/v2/users", user)
return response.data
}

export const getBuildInfo = async (): Promise<Types.BuildInfoResponse> => {
export const getBuildInfo = async (): Promise<TypesGen.BuildInfoResponse> => {
const response = await axios.get("/api/v2/buildinfo")
return response.data
}

export const putWorkspaceAutostart = async (
workspaceID: string,
autostart: Types.WorkspaceAutostartRequest,
autostart: TypesGen.UpdateWorkspaceAutostartRequest,
): Promise<void> => {
const payload = JSON.stringify(autostart)
await axios.put(`/api/v2/workspaces/${workspaceID}/autostart`, payload, {
Expand All @@ -138,15 +146,18 @@ export const putWorkspaceAutostart = async (

export const putWorkspaceAutostop = async (
workspaceID: string,
autostop: Types.WorkspaceAutostopRequest,
autostop: TypesGen.UpdateWorkspaceAutostopRequest,
): Promise<void> => {
const payload = JSON.stringify(autostop)
await axios.put(`/api/v2/workspaces/${workspaceID}/autostop`, payload, {
headers: { ...CONTENT_TYPE_JSON },
})
}

export const updateProfile = async (userId: string, data: Types.UpdateProfileRequest): Promise<Types.UserResponse> => {
export const updateProfile = async (
userId: string,
data: TypesGen.UpdateUserProfileRequest,
): Promise<TypesGen.User> => {
const response = await axios.put(`/api/v2/users/${userId}/profile`, data)
return response.data
}
Expand Down
111 changes: 0 additions & 111 deletions site/src/api/types.ts
Original file line number Diff line number Diff line change
@@ -1,121 +1,10 @@
/**
* `BuildInfoResponse` must be kept in sync with the go struct in buildinfo.go.
*/
export interface BuildInfoResponse {
external_url: string
version: string
}

export interface LoginResponse {
session_token: string
}

export interface CreateUserRequest {
username: string
email: string
password: string
organization_id: string
}

export interface UserResponse {
readonly id: string
readonly username: string
readonly email: string
readonly created_at: string
readonly status: "active" | "suspended"
readonly organization_ids: string[]
readonly roles: { name: string; display_name: string }[]
}

/**
* `Organization` must be kept in sync with the go struct in organizations.go
*/
export interface Organization {
id: string
name: string
created_at: string
updated_at: string
}

export interface Provisioner {
id: string
name: string
}

// This must be kept in sync with the `Template` struct in the back-end
export interface Template {
id: string
created_at: string
updated_at: string
organization_id: string
name: string
provisioner: string
active_version_id: string
}

export interface CreateTemplateRequest {
name: string
organizationId: string
provisioner: string
}

export interface CreateWorkspaceRequest {
name: string
template_id: string
organization_id: string
}

export interface WorkspaceBuild {
id: string
}

export interface Workspace {
id: string
created_at: string
updated_at: string
owner_id: string
template_id: string
name: string
autostart_schedule: string
autostop_schedule: string
latest_build: WorkspaceBuild
}

export interface WorkspaceResource {
id: string
agents?: WorkspaceAgent[]
}

export interface WorkspaceAgent {
id: string
name: string
operating_system: string
}

export interface APIKeyResponse {
key: string
}

export interface UserAgent {
readonly browser: string
readonly device: string
readonly ip_address: string
readonly os: string
}

export interface WorkspaceAutostartRequest {
schedule: string
}

export interface WorkspaceAutostopRequest {
schedule: string
}

export interface UpdateProfileRequest {
readonly username: string
readonly email: string
}

export interface ReconnectingPTYRequest {
readonly data?: string
readonly height?: number
Expand Down
Loading