Skip to content

Commit 26b04cc

Browse files
authored
chore: switch to generated types (#1394)
* Make column renderer use the same type as its key That way the renderer only takes `string` for example when rendering the name field instead of `string | number` when the interface has some fields that are strings and some fields are numbers. This will be necessary when switching to generated types since some of the fields are numbers (like the owner count on a template). * Switch fully to generated types In some places the organization ID is part of the URL but not part of the request so I separated out the ID into a separate argument in the relevant API functions. Otherwise this was a straightforward replacement where I mostly only needed to change some of the interface names (User instead of UserResponse for example) and add a few missing but required properties. I kind of winged the template form; I am not sure what the difference between a template and template version is or why the latter comes before the former so the form just returns all the data required to create both. * Delete handwritten types Except for UserAgent which seems to be purely frontend and ReconnectingPTYRequest which is not in codersdk so I am just leaving it for now. * Remove implemented omitempty as a future idea This was implemented in 2d3dc43. * Add missing optionalities to generated request interfaces
1 parent 56076a0 commit 26b04cc

File tree

30 files changed

+235
-298
lines changed

30 files changed

+235
-298
lines changed

codersdk/organizations.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ type Organization struct {
2424
// CreateTemplateVersionRequest enables callers to create a new Template Version.
2525
type CreateTemplateVersionRequest struct {
2626
// TemplateID optionally associates a version with a template.
27-
TemplateID uuid.UUID `json:"template_id"`
27+
TemplateID uuid.UUID `json:"template_id,omitempty"`
2828

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

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

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

6060
func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization, error) {

codersdk/users.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ const (
2121
)
2222

2323
type UsersRequest struct {
24-
Search string `json:"search"`
24+
Search string `json:"search,omitempty"`
2525
// Filter users by status
26-
Status string `json:"status"`
26+
Status string `json:"status,omitempty"`
2727
Pagination
2828
}
2929

codersdk/workspaces.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ type Workspace struct {
3131

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

scripts/apitypings/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This main.go generates typescript types from the codersdk types in Go.
55
# Features
66

77
- Supports Go types
8-
- [x] Basics (string/int/etc)
8+
- [x] Basics (string/int/etc)
99
- [x] Maps
1010
- [x] Slices
1111
- [x] Enums
@@ -36,5 +36,4 @@ type InternalType struct {
3636

3737
# Future Ideas
3838

39-
- Should `omitempty` in the `json` tag indicate optional?
4039
- Use a yaml config for overriding certain types

site/src/api/api.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import axios from "axios"
22
import { getApiKey, login, logout } from "./api"
3-
import { APIKeyResponse, LoginResponse } from "./types"
3+
import * as TypesGen from "./typesGenerated"
44

55
// Mock the axios module so that no real network requests are made, but rather
66
// we swap in a resolved or rejected value
@@ -12,7 +12,7 @@ describe("api.ts", () => {
1212
describe("login", () => {
1313
it("should return LoginResponse", async () => {
1414
// given
15-
const loginResponse: LoginResponse = {
15+
const loginResponse: TypesGen.LoginWithPasswordResponse = {
1616
session_token: "abc_123_test",
1717
}
1818
const axiosMockPost = jest.fn().mockImplementationOnce(() => {
@@ -87,7 +87,7 @@ describe("api.ts", () => {
8787
describe("getApiKey", () => {
8888
it("should return APIKeyResponse", async () => {
8989
// given
90-
const apiKeyResponse: APIKeyResponse = {
90+
const apiKeyResponse: TypesGen.GenerateAPIKeyResponse = {
9191
key: "abc_123_test",
9292
}
9393
const axiosMockPost = jest.fn().mockImplementationOnce(() => {

site/src/api/api.ts

+38-27
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,32 @@
11
import axios, { AxiosRequestHeaders } from "axios"
22
import { mutate } from "swr"
3-
import * as Types from "./types"
43
import * as TypesGen from "./typesGenerated"
54

65
const CONTENT_TYPE_JSON: AxiosRequestHeaders = {
76
"Content-Type": "application/json",
87
}
98

10-
export const provisioners: Types.Provisioner[] = [
9+
export const provisioners: TypesGen.ProvisionerDaemon[] = [
1110
{
1211
id: "terraform",
1312
name: "Terraform",
13+
created_at: "",
14+
provisioners: [],
1415
},
1516
{
1617
id: "cdr-basic",
1718
name: "Basic",
19+
created_at: "",
20+
provisioners: [],
1821
},
1922
]
2023

2124
export namespace Workspace {
22-
export const create = async (request: Types.CreateWorkspaceRequest): Promise<Types.Workspace> => {
23-
const response = await fetch(`/api/v2/organizations/${request.organization_id}/workspaces`, {
25+
export const create = async (
26+
organizationId: string,
27+
request: TypesGen.CreateWorkspaceRequest,
28+
): Promise<TypesGen.Workspace> => {
29+
const response = await fetch(`/api/v2/organizations/${organizationId}/workspaces`, {
2430
method: "POST",
2531
headers: {
2632
"Content-Type": "application/json",
@@ -43,13 +49,13 @@ export namespace Workspace {
4349
}
4450
}
4551

46-
export const login = async (email: string, password: string): Promise<Types.LoginResponse> => {
52+
export const login = async (email: string, password: string): Promise<TypesGen.LoginWithPasswordResponse> => {
4753
const payload = JSON.stringify({
4854
email,
4955
password,
5056
})
5157

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

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

63-
export const getUser = async (): Promise<Types.UserResponse> => {
64-
const response = await axios.get<Types.UserResponse>("/api/v2/users/me")
69+
export const getUser = async (): Promise<TypesGen.User> => {
70+
const response = await axios.get<TypesGen.User>("/api/v2/users/me")
6571
return response.data
6672
}
6773

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

73-
export const getApiKey = async (): Promise<Types.APIKeyResponse> => {
74-
const response = await axios.post<Types.APIKeyResponse>("/api/v2/users/me/keys")
79+
export const getApiKey = async (): Promise<TypesGen.GenerateAPIKeyResponse> => {
80+
const response = await axios.post<TypesGen.GenerateAPIKeyResponse>("/api/v2/users/me/keys")
7581
return response.data
7682
}
7783

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

83-
export const getOrganization = async (organizationId: string): Promise<Types.Organization> => {
84-
const response = await axios.get<Types.Organization>(`/api/v2/organizations/${organizationId}`)
89+
export const getOrganization = async (organizationId: string): Promise<TypesGen.Organization> => {
90+
const response = await axios.get<TypesGen.Organization>(`/api/v2/organizations/${organizationId}`)
8591
return response.data
8692
}
8793

88-
export const getOrganizations = async (): Promise<Types.Organization[]> => {
89-
const response = await axios.get<Types.Organization[]>("/api/v2/users/me/organizations")
94+
export const getOrganizations = async (): Promise<TypesGen.Organization[]> => {
95+
const response = await axios.get<TypesGen.Organization[]>("/api/v2/users/me/organizations")
9096
return response.data
9197
}
9298

93-
export const getTemplate = async (templateId: string): Promise<Types.Template> => {
94-
const response = await axios.get<Types.Template>(`/api/v2/templates/${templateId}`)
99+
export const getTemplate = async (templateId: string): Promise<TypesGen.Template> => {
100+
const response = await axios.get<TypesGen.Template>(`/api/v2/templates/${templateId}`)
95101
return response.data
96102
}
97103

98-
export const getWorkspace = async (workspaceId: string): Promise<Types.Workspace> => {
99-
const response = await axios.get<Types.Workspace>(`/api/v2/workspaces/${workspaceId}`)
104+
export const getWorkspace = async (workspaceId: string): Promise<TypesGen.Workspace> => {
105+
const response = await axios.get<TypesGen.Workspace>(`/api/v2/workspaces/${workspaceId}`)
100106
return response.data
101107
}
102108

103109
export const getWorkspaceByOwnerAndName = async (
104110
organizationID: string,
105111
username = "me",
106112
workspaceName: string,
107-
): Promise<Types.Workspace> => {
108-
const response = await axios.get<Types.Workspace>(
113+
): Promise<TypesGen.Workspace> => {
114+
const response = await axios.get<TypesGen.Workspace>(
109115
`/api/v2/organizations/${organizationID}/workspaces/${username}/${workspaceName}`,
110116
)
111117
return response.data
112118
}
113119

114-
export const getWorkspaceResources = async (workspaceBuildID: string): Promise<Types.WorkspaceResource[]> => {
115-
const response = await axios.get<Types.WorkspaceResource[]>(`/api/v2/workspacebuilds/${workspaceBuildID}/resources`)
120+
export const getWorkspaceResources = async (workspaceBuildID: string): Promise<TypesGen.WorkspaceResource[]> => {
121+
const response = await axios.get<TypesGen.WorkspaceResource[]>(
122+
`/api/v2/workspacebuilds/${workspaceBuildID}/resources`,
123+
)
116124
return response.data
117125
}
118126

119-
export const createUser = async (user: Types.CreateUserRequest): Promise<TypesGen.User> => {
127+
export const createUser = async (user: TypesGen.CreateUserRequest): Promise<TypesGen.User> => {
120128
const response = await axios.post<TypesGen.User>("/api/v2/users", user)
121129
return response.data
122130
}
123131

124-
export const getBuildInfo = async (): Promise<Types.BuildInfoResponse> => {
132+
export const getBuildInfo = async (): Promise<TypesGen.BuildInfoResponse> => {
125133
const response = await axios.get("/api/v2/buildinfo")
126134
return response.data
127135
}
128136

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

139147
export const putWorkspaceAutostop = async (
140148
workspaceID: string,
141-
autostop: Types.WorkspaceAutostopRequest,
149+
autostop: TypesGen.UpdateWorkspaceAutostopRequest,
142150
): Promise<void> => {
143151
const payload = JSON.stringify(autostop)
144152
await axios.put(`/api/v2/workspaces/${workspaceID}/autostop`, payload, {
145153
headers: { ...CONTENT_TYPE_JSON },
146154
})
147155
}
148156

149-
export const updateProfile = async (userId: string, data: Types.UpdateProfileRequest): Promise<Types.UserResponse> => {
157+
export const updateProfile = async (
158+
userId: string,
159+
data: TypesGen.UpdateUserProfileRequest,
160+
): Promise<TypesGen.User> => {
150161
const response = await axios.put(`/api/v2/users/${userId}/profile`, data)
151162
return response.data
152163
}

site/src/api/types.ts

-111
Original file line numberDiff line numberDiff line change
@@ -1,121 +1,10 @@
1-
/**
2-
* `BuildInfoResponse` must be kept in sync with the go struct in buildinfo.go.
3-
*/
4-
export interface BuildInfoResponse {
5-
external_url: string
6-
version: string
7-
}
8-
9-
export interface LoginResponse {
10-
session_token: string
11-
}
12-
13-
export interface CreateUserRequest {
14-
username: string
15-
email: string
16-
password: string
17-
organization_id: string
18-
}
19-
20-
export interface UserResponse {
21-
readonly id: string
22-
readonly username: string
23-
readonly email: string
24-
readonly created_at: string
25-
readonly status: "active" | "suspended"
26-
readonly organization_ids: string[]
27-
readonly roles: { name: string; display_name: string }[]
28-
}
29-
30-
/**
31-
* `Organization` must be kept in sync with the go struct in organizations.go
32-
*/
33-
export interface Organization {
34-
id: string
35-
name: string
36-
created_at: string
37-
updated_at: string
38-
}
39-
40-
export interface Provisioner {
41-
id: string
42-
name: string
43-
}
44-
45-
// This must be kept in sync with the `Template` struct in the back-end
46-
export interface Template {
47-
id: string
48-
created_at: string
49-
updated_at: string
50-
organization_id: string
51-
name: string
52-
provisioner: string
53-
active_version_id: string
54-
}
55-
56-
export interface CreateTemplateRequest {
57-
name: string
58-
organizationId: string
59-
provisioner: string
60-
}
61-
62-
export interface CreateWorkspaceRequest {
63-
name: string
64-
template_id: string
65-
organization_id: string
66-
}
67-
68-
export interface WorkspaceBuild {
69-
id: string
70-
}
71-
72-
export interface Workspace {
73-
id: string
74-
created_at: string
75-
updated_at: string
76-
owner_id: string
77-
template_id: string
78-
name: string
79-
autostart_schedule: string
80-
autostop_schedule: string
81-
latest_build: WorkspaceBuild
82-
}
83-
84-
export interface WorkspaceResource {
85-
id: string
86-
agents?: WorkspaceAgent[]
87-
}
88-
89-
export interface WorkspaceAgent {
90-
id: string
91-
name: string
92-
operating_system: string
93-
}
94-
95-
export interface APIKeyResponse {
96-
key: string
97-
}
98-
991
export interface UserAgent {
1002
readonly browser: string
1013
readonly device: string
1024
readonly ip_address: string
1035
readonly os: string
1046
}
1057

106-
export interface WorkspaceAutostartRequest {
107-
schedule: string
108-
}
109-
110-
export interface WorkspaceAutostopRequest {
111-
schedule: string
112-
}
113-
114-
export interface UpdateProfileRequest {
115-
readonly username: string
116-
readonly email: string
117-
}
118-
1198
export interface ReconnectingPTYRequest {
1209
readonly data?: string
12110
readonly height?: number

0 commit comments

Comments
 (0)