From f0be8853b21103718321bb45ec408a9c031aa8a3 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 6 May 2022 08:22:19 +0000 Subject: [PATCH 1/6] feat: Add support for json omitempty to apitypings --- codersdk/pagination.go | 6 +++--- scripts/apitypings/main.go | 6 +++++- site/src/api/typesGenerated.ts | 18 +++++++++--------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/codersdk/pagination.go b/codersdk/pagination.go index 81d15f1083023..a4adee6b6e567 100644 --- a/codersdk/pagination.go +++ b/codersdk/pagination.go @@ -14,16 +14,16 @@ type Pagination struct { // Offset for better performance. To use it as an alternative, // set AfterID to the last UUID returned by the previous // request. - AfterID uuid.UUID `json:"after_id"` + AfterID uuid.UUID `json:"after_id,omitempty"` // Limit sets the maximum number of users to be returned // in a single page. If the limit is <= 0, there is no limit // and all users are returned. - Limit int `json:"limit"` + Limit int `json:"limit,omitempty"` // Offset is used to indicate which page to return. An offset of 0 // returns the first 'limit' number of users. // To get the next page, use offset=*. // Offset is 0 indexed, so the first record sits at offset 0. - Offset int `json:"offset"` + Offset int `json:"offset,omitempty"` } // asRequestOption returns a function that can be used in (*Client).request. diff --git a/scripts/apitypings/main.go b/scripts/apitypings/main.go index 529b7ae0f42f2..6dd05cf0d15e4 100644 --- a/scripts/apitypings/main.go +++ b/scripts/apitypings/main.go @@ -251,6 +251,10 @@ func (g *Generator) buildStruct(obj types.Object, st *types.Struct) (string, err if jsonName == "" { jsonName = field.Name() } + jsonOptional := false + if len(arr) > 1 && arr[1] == "omitempty" { + jsonOptional = true + } var tsType TypescriptType // If a `typescript:"string"` exists, we take this, and do not try to infer. @@ -273,7 +277,7 @@ func (g *Generator) buildStruct(obj types.Object, st *types.Struct) (string, err _, _ = s.WriteRune('\n') } optional := "" - if tsType.Optional { + if jsonOptional || tsType.Optional { optional = "?" } _, _ = s.WriteString(fmt.Sprintf("%sreadonly %s%s: %s\n", indent, jsonName, optional, tsType.ValueType)) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index d689bf1b49080..ce329d4ade05e 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -91,7 +91,7 @@ export interface CreateWorkspaceBuildRequest { // This is likely an enum in an external package ("github.com/coder/coder/coderd/database.WorkspaceTransition") readonly transition: string readonly dry_run: boolean - readonly state: string + readonly state?: string } // From codersdk/organizations.go:52:6 @@ -149,9 +149,9 @@ export interface OrganizationMember { // From codersdk/pagination.go:11:6 export interface Pagination { - readonly after_id: string - readonly limit: number - readonly offset: number + readonly after_id?: string + readonly limit?: number + readonly offset?: number } // From codersdk/parameters.go:26:6 @@ -185,7 +185,7 @@ export interface ProvisionerJob { readonly created_at: string readonly started_at?: string readonly completed_at?: string - readonly error: string + readonly error?: string readonly status: ProvisionerJobStatus readonly worker_id?: string } @@ -355,12 +355,12 @@ export interface WorkspaceAgent { readonly status: WorkspaceAgentStatus readonly name: string readonly resource_id: string - readonly instance_id: string + readonly instance_id?: string readonly architecture: string readonly environment_variables: Record readonly operating_system: string - readonly startup_script: string - readonly directory: string + readonly startup_script?: string + readonly directory?: string } // From codersdk/workspaceagents.go:47:6 @@ -415,7 +415,7 @@ export interface WorkspaceResource { readonly workspace_transition: string readonly type: string readonly name: string - readonly agents: WorkspaceAgent[] + readonly agents?: WorkspaceAgent[] } // From codersdk/parameters.go:16:6 From a723e32395f75bfec3e0cce4814f776f1951a4a6 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 6 May 2022 09:05:37 +0000 Subject: [PATCH 2/6] feat: Express embedded structs via extends in TypeScript --- scripts/apitypings/main.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/scripts/apitypings/main.go b/scripts/apitypings/main.go index 6dd05cf0d15e4..565c7603fc764 100644 --- a/scripts/apitypings/main.go +++ b/scripts/apitypings/main.go @@ -237,10 +237,28 @@ func (g *Generator) posLine(obj types.Object) string { func (g *Generator) buildStruct(obj types.Object, st *types.Struct) (string, error) { var s strings.Builder _, _ = s.WriteString(g.posLine(obj)) + _, _ = s.WriteString(fmt.Sprintf("export interface %s ", obj.Name())) - _, _ = s.WriteString(fmt.Sprintf("export interface %s {\n", obj.Name())) + // Handle named embedded structs in the codersdk package via extension. + var extends []string + extendedFields := make(map[int]bool) + for i := 0; i < st.NumFields(); i++ { + field := st.Field(i) + if field.Embedded() && field.Pkg().Name() == "codersdk" { + extendedFields[i] = true + extends = append(extends, field.Name()) + } + } + if len(extends) > 0 { + _, _ = s.WriteString(fmt.Sprintf("extends %s ", strings.Join(extends, ", "))) + } + + _, _ = s.WriteString("{\n") // For each field in the struct, we print 1 line of the typescript interface for i := 0; i < st.NumFields(); i++ { + if extendedFields[i] { + continue + } field := st.Field(i) tag := reflect.StructTag(st.Tag(i)) @@ -326,7 +344,7 @@ func (g *Generator) typescriptType(ty types.Type) (TypescriptType, error) { return TypescriptType{ ValueType: "any", AboveTypeLine: fmt.Sprintf("%s\n%s", - indentedComment("Embedded struct, please fix by naming it"), + indentedComment("Embedded anonymous struct, please fix by naming it"), indentedComment("eslint-disable-next-line @typescript-eslint/no-explicit-any"), ), }, nil From 5cc68c919833ea88ec74fab90f850b35ad4d0541 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 6 May 2022 09:07:13 +0000 Subject: [PATCH 3/6] Mark typesGenerated.ts as PHONY in Makefile Otherwise changes in the apitypings generator are not detected. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index ffc08fe329732..55ce78e15c2aa 100644 --- a/Makefile +++ b/Makefile @@ -86,6 +86,7 @@ site/out/index.html: $(shell find ./site -not -path './site/node_modules/*' -typ site/src/api/typesGenerated.ts: $(shell find codersdk -type f -name '*.go') go run scripts/apitypings/main.go > site/src/api/typesGenerated.ts cd site && yarn run format:types +.PHONY: site/src/api/typesGenerated.ts test: gotestsum -- -v -short ./... From b832fdb6b2c2eb544f86a9682412ec39ba73170b Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 6 May 2022 09:20:19 +0000 Subject: [PATCH 4/6] Handle unembedding via json field in apitypings --- scripts/apitypings/main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/apitypings/main.go b/scripts/apitypings/main.go index 565c7603fc764..0891439e723e3 100644 --- a/scripts/apitypings/main.go +++ b/scripts/apitypings/main.go @@ -244,7 +244,10 @@ func (g *Generator) buildStruct(obj types.Object, st *types.Struct) (string, err extendedFields := make(map[int]bool) for i := 0; i < st.NumFields(); i++ { field := st.Field(i) - if field.Embedded() && field.Pkg().Name() == "codersdk" { + tag := reflect.StructTag(st.Tag(i)) + // Adding a json struct tag causes the json package to consider + // the field unembedded. + if field.Embedded() && tag.Get("json") == "" && field.Pkg().Name() == "codersdk" { extendedFields[i] = true extends = append(extends, field.Name()) } From 0e0395d28a9a7081f0408494ec79e93efd318d7c Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Fri, 6 May 2022 17:48:04 +0000 Subject: [PATCH 5/6] Add scripts/apitypings/main.go to Makefile instead of using .PHONY --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 55ce78e15c2aa..9ea8aa90a13b0 100644 --- a/Makefile +++ b/Makefile @@ -83,10 +83,9 @@ site/out/index.html: $(shell find ./site -not -path './site/node_modules/*' -typ # Restores GITKEEP files! git checkout HEAD site/out -site/src/api/typesGenerated.ts: $(shell find codersdk -type f -name '*.go') +site/src/api/typesGenerated.ts: scripts/apitypings/main.go $(shell find codersdk -type f -name '*.go') go run scripts/apitypings/main.go > site/src/api/typesGenerated.ts cd site && yarn run format:types -.PHONY: site/src/api/typesGenerated.ts test: gotestsum -- -v -short ./... From 3650ed78a7760a11091a19f408e3ef1f2636a53c Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 10 May 2022 07:32:56 +0000 Subject: [PATCH 6/6] Run make gen --- site/src/api/typesGenerated.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index ce329d4ade05e..e30c94e8663e6 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -264,9 +264,8 @@ export interface TemplateVersionParameterSchema { } // From codersdk/templates.go:74:6 -export interface TemplateVersionsByTemplateRequest { +export interface TemplateVersionsByTemplateRequest extends Pagination { readonly template_id: string - readonly Pagination: Pagination } // From codersdk/templates.go:28:6 @@ -323,10 +322,9 @@ export interface UserRoles { } // From codersdk/users.go:23:6 -export interface UsersRequest { +export interface UsersRequest extends Pagination { readonly search: string readonly status: string - readonly Pagination: Pagination } // From codersdk/workspaces.go:18:6