Skip to content

chore: switch to generated types #1394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 12, 2022
Merged
Prev Previous commit
Next Next commit
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.

Arguably `parameter_values` could be made optional but I am just passing
in an empty array for now mostly just to make it obvious that we could
pass stuff there if we need to.

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.
  • Loading branch information
code-asher committed May 11, 2022
commit 60323d8c6dcbd08865f5e6cf0bf04fffcc4e07ca
6 changes: 3 additions & 3 deletions site/src/api/index.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 "."
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/index.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
10 changes: 5 additions & 5 deletions site/src/components/CreateUserForm/CreateUserForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import TextField from "@material-ui/core/TextField"
import { FormikContextType, FormikErrors, useFormik } from "formik"
import React from "react"
import * as Yup from "yup"
import { CreateUserRequest } from "../../api/types"
import * as TypesGen from "../../api/typesGenerated"
import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils"
import { FormFooter } from "../FormFooter/FormFooter"
import { FullPageForm } from "../FullPageForm/FullPageForm"
Expand All @@ -21,9 +21,9 @@ export const Language = {
}

export interface CreateUserFormProps {
onSubmit: (user: CreateUserRequest) => void
onSubmit: (user: TypesGen.CreateUserRequest) => void
onCancel: () => void
formErrors?: FormikErrors<CreateUserRequest>
formErrors?: FormikErrors<TypesGen.CreateUserRequest>
isLoading: boolean
error?: string
myOrgId: string
Expand All @@ -43,7 +43,7 @@ export const CreateUserForm: React.FC<CreateUserFormProps> = ({
error,
myOrgId,
}) => {
const form: FormikContextType<CreateUserRequest> = useFormik<CreateUserRequest>({
const form: FormikContextType<TypesGen.CreateUserRequest> = useFormik<TypesGen.CreateUserRequest>({
initialValues: {
email: "",
password: "",
Expand All @@ -53,7 +53,7 @@ export const CreateUserForm: React.FC<CreateUserFormProps> = ({
validationSchema,
onSubmit,
})
const getFieldHelpers = getFormHelpers<CreateUserRequest>(form, formErrors)
const getFieldHelpers = getFormHelpers<TypesGen.CreateUserRequest>(form, formErrors)

return (
<FullPageForm title="Create user" onCancel={onCancel}>
Expand Down
4 changes: 2 additions & 2 deletions site/src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { makeStyles } from "@material-ui/core/styles"
import Typography from "@material-ui/core/Typography"
import { useActor } from "@xstate/react"
import React, { useContext } from "react"
import { BuildInfoResponse } from "../../api/types"
import * as TypesGen from "../../api/typesGenerated"
import { XServiceContext } from "../../xServices/StateContext"

export const Language = {
buildInfoText: (buildInfo: BuildInfoResponse): string => {
buildInfoText: (buildInfo: TypesGen.BuildInfoResponse): string => {
return `Coder ${buildInfo.version}`
},
}
Expand Down
4 changes: 2 additions & 2 deletions site/src/components/NavbarView/NavbarView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import ListItem from "@material-ui/core/ListItem"
import { fade, makeStyles } from "@material-ui/core/styles"
import React from "react"
import { NavLink } from "react-router-dom"
import { UserResponse } from "../../api/types"
import * as TypesGen from "../../api/typesGenerated"
import { navHeight } from "../../theme/constants"
import { AdminDropdown } from "../AdminDropdown/AdminDropdown"
import { Logo } from "../Icons/Logo"
import { UserDropdown } from "../UserDropdown/UsersDropdown"

export interface NavbarViewProps {
user?: UserResponse
user?: TypesGen.User
onSignOut: () => void
}

Expand Down
4 changes: 2 additions & 2 deletions site/src/components/UserDropdown/UsersDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { fade, makeStyles } from "@material-ui/core/styles"
import AccountIcon from "@material-ui/icons/AccountCircleOutlined"
import React, { useState } from "react"
import { Link } from "react-router-dom"
import { UserResponse } from "../../api/types"
import * as TypesGen from "../../api/typesGenerated"
import { BorderedMenu } from "../BorderedMenu/BorderedMenu"
import { CloseDropdown, OpenDropdown } from "../DropdownArrows/DropdownArrows"
import { DocsIcon } from "../Icons/DocsIcon"
Expand All @@ -21,7 +21,7 @@ export const Language = {
signOutLabel: "Sign Out",
}
export interface UserDropdownProps {
user: UserResponse
user: TypesGen.User
onSignOut: () => void
}

Expand Down
4 changes: 2 additions & 2 deletions site/src/components/UserProfileCard/UserProfileCard.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { makeStyles } from "@material-ui/core/styles"
import Typography from "@material-ui/core/Typography"
import React from "react"
import { UserResponse } from "../../api/types"
import * as TypesGen from "../../api/typesGenerated"
import { UserAvatar } from "../UserAvatar/UserAvatar"

interface UserProfileCardProps {
user: UserResponse
user: TypesGen.User
}

export const UserProfileCard: React.FC<UserProfileCardProps> = ({ user }) => {
Expand Down
9 changes: 4 additions & 5 deletions site/src/components/UsersTable/UsersTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import TableCell from "@material-ui/core/TableCell"
import TableHead from "@material-ui/core/TableHead"
import TableRow from "@material-ui/core/TableRow"
import React from "react"
import { UserResponse } from "../../api/types"
import * as TypesGen from "../../api/typesGenerated"
import { EmptyState } from "../EmptyState/EmptyState"
import { RoleSelect } from "../RoleSelect/RoleSelect"
Expand All @@ -25,10 +24,10 @@ export const Language = {
}

export interface UsersTableProps {
users: UserResponse[]
onSuspendUser: (user: UserResponse) => void
onResetUserPassword: (user: UserResponse) => void
onUpdateUserRoles: (user: UserResponse, roles: TypesGen.Role["name"][]) => void
users: TypesGen.User[]
onSuspendUser: (user: TypesGen.User) => void
onResetUserPassword: (user: TypesGen.User) => void
onUpdateUserRoles: (user: TypesGen.User, roles: TypesGen.Role["name"][]) => void
roles: TypesGen.Role[]
isUpdatingUserRoles?: boolean
}
Expand Down
8 changes: 4 additions & 4 deletions site/src/components/Workspace/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import Typography from "@material-ui/core/Typography"
import CloudCircleIcon from "@material-ui/icons/CloudCircle"
import React from "react"
import { Link } from "react-router-dom"
import * as Types from "../../api/types"
import * as TypesGen from "../../api/typesGenerated"
import { WorkspaceSchedule } from "../WorkspaceSchedule/WorkspaceSchedule"
import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection"
import * as Constants from "./constants"

export interface WorkspaceProps {
organization: Types.Organization
workspace: Types.Workspace
template: Types.Template
organization: TypesGen.Organization
workspace: TypesGen.Workspace
template: TypesGen.Template
}

/**
Expand Down
26 changes: 20 additions & 6 deletions site/src/forms/CreateTemplateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { makeStyles } from "@material-ui/core/styles"
import { FormikContextType, useFormik } from "formik"
import React from "react"
import * as Yup from "yup"
import { CreateTemplateRequest, Organization, Provisioner, Template } from "../api/types"
import * as TypesGen from "../api/typesGenerated"
import { FormCloseButton } from "../components/FormCloseButton/FormCloseButton"
import { FormDropdownField, FormDropdownItem } from "../components/FormDropdownField/FormDropdownField"
import { FormSection } from "../components/FormSection/FormSection"
Expand All @@ -12,13 +12,21 @@ import { FormTitle } from "../components/FormTitle/FormTitle"
import { LoadingButton } from "../components/LoadingButton/LoadingButton"
import { maxWidth } from "../theme/constants"

// It appears that to create a template you need to create a template version
// and then a template so this contains the information to do both.
export type CreateTemplateRequest = TypesGen.CreateTemplateVersionRequest & Pick<TypesGen.CreateTemplateRequest, "name">

export interface CreateTemplateFormProps {
provisioners: Provisioner[]
organizations: Organization[]
onSubmit: (request: CreateTemplateRequest) => Promise<Template>
provisioners: TypesGen.ProvisionerDaemon[]
organizations: TypesGen.Organization[]
onSubmit: (organizationId: string, request: CreateTemplateRequest) => Promise<TypesGen.Template>
onCancel: () => void
}

interface CreateTemplateFields extends Pick<CreateTemplateRequest, "name" | "provisioner"> {
organizationId: string
}

const validationSchema = Yup.object({
provisioner: Yup.string().required("Provisioner is required."),
organizationId: Yup.string().required("Organization is required."),
Expand All @@ -33,7 +41,7 @@ export const CreateTemplateForm: React.FC<CreateTemplateFormProps> = ({
}) => {
const styles = useStyles()

const form: FormikContextType<CreateTemplateRequest> = useFormik<CreateTemplateRequest>({
const form: FormikContextType<CreateTemplateFields> = useFormik<CreateTemplateFields>({
initialValues: {
provisioner: provisioners[0].id,
organizationId: organizations[0].name,
Expand All @@ -42,7 +50,13 @@ export const CreateTemplateForm: React.FC<CreateTemplateFormProps> = ({
enableReinitialize: true,
validationSchema: validationSchema,
onSubmit: (req) => {
return onSubmit(req)
return onSubmit(req.organizationId, {
name: req.name,
storage_method: "file",
storage_source: "hash",
provisioner: req.provisioner,
parameter_values: [],
})
},
})

Expand Down
Loading