Skip to content

feat: generate typescript types from codersdk structs #1047

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 24 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat: experiment: generate typescript types from codersdk structs
  • Loading branch information
f0ssel committed Apr 18, 2022
commit 175e9c21a7385fbf6e1d702dce855c575dc647bb
104 changes: 104 additions & 0 deletions coderts/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package main
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just put this in cmd/typings or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that but think this fits much more as a "generate.sh" type of script. I connected it to our make gen command, which I think fits nicer than making it a top level command.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It breaks the idiom of all commands being inside the cmd folder. We have the DataDog CI report helper in scripts/datadog-cireport, so it could be there too!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's not a command it's just a script that happens to be go. I'll move to scripts if we want. Is this really any different than the scripts in the coderd/database package? Or should those move into scripts as well ideally?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think dump should totally move into scripts too. It's just nice to know whether a package should be imported or not based on folder structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool I moved it


import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"log"
"os"
"path/filepath"
"strings"

"golang.org/x/xerrors"
)

const (
baseDir = "./codersdk"
)

func main() {
err := run()
if err != nil {
log.Fatal(err)
}
}

func run() error {
fset := token.NewFileSet()
entries, err := os.ReadDir(baseDir)
if err != nil {
return err
}

for _, entry := range entries {
goFile, err := parser.ParseFile(fset, filepath.Join(baseDir, entry.Name()), nil, 0)
if err != nil {
return err
}

for _, node := range goFile.Decls {
switch node.(type) {

case *ast.GenDecl:
genDecl := node.(*ast.GenDecl)
for _, spec := range genDecl.Specs {
switch spec.(type) {
case *ast.TypeSpec:
typeSpec := spec.(*ast.TypeSpec)
s, err := writeStruct(typeSpec)
if err != nil {
continue
}

fmt.Printf(s)
}
}
}
}
}

return nil
}

func writeStruct(typeSpec *ast.TypeSpec) (string, error) {
s := fmt.Sprintf("export interface %s {\n", typeSpec.Name.Name)
jsonFields := 0
switch typeSpec.Type.(type) {
case *ast.StructType:
structType := typeSpec.Type.(*ast.StructType)
for _, field := range structType.Fields.List {
i, ok := field.Type.(*ast.Ident)
if !ok {
continue
}
fieldType := i.Name
fieldName := ""
if field.Tag != nil && field.Tag.Value != "" {
for _, pair := range strings.Split(field.Tag.Value, " ") {
if strings.HasPrefix(pair, "`json:\"") {
fieldName = strings.TrimPrefix(pair, "`json:\"")
fieldName = strings.Split(fieldName, ",")[0]
fieldName = strings.TrimSuffix(fieldName, "`")
fieldName = strings.TrimSuffix(fieldName, "\"")
break
}
}
}
if fieldName == "" {
break
}

s = fmt.Sprintf("%s %s: %s\n", s, fieldName, fieldType)
jsonFields++
}
default:
return "", xerrors.New("not struct")
}

if jsonFields == 0 {
return "", xerrors.New("no json fields")
}

return fmt.Sprintf("%s}\n\n", s), nil
}
172 changes: 172 additions & 0 deletions coderts/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
export interface BuildInfoResponse {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make these link directly to the Go files too... could be neat for documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working on generating docs, may happen in this PR or a follow up.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added the doc codelines

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is cool af

external_url: string
version: string
}

export interface UploadResponse {
hash: string
}

export interface GitSSHKey {
public_key: string
}

export interface AgentGitSSHKey {
private_key: string
}

export interface Organization {
name: string
}

export interface CreateTemplateVersionRequest {
storage_source: string
}

export interface CreateTemplateRequest {
name: string
}

export interface CreateParameterRequest {
name: string
source_value: string
}

export interface ProvisionerJob {
error: string
status: ProvisionerJobStatus
}

export interface ProvisionerJobLog {
stage: string
output: string
}

export interface Template {
name: string
workspace_owner_count: uint32
}

export interface TemplateVersion {
name: string
job: ProvisionerJob
}

export interface User {
email: string
username: string
name: string
}

export interface CreateFirstUserRequest {
email: string
username: string
password: string
organization: string
}

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

export interface UpdateUserProfileRequest {
email: string
username: string
}

export interface LoginWithPasswordRequest {
email: string
password: string
}

export interface LoginWithPasswordResponse {
session_token: string
}

export interface GenerateAPIKeyResponse {
key: string
}

export interface CreateOrganizationRequest {
name: string
}

export interface CreateWorkspaceRequest {
name: string
}

export interface GoogleInstanceIdentityToken {
json_web_token: string
}

export interface AWSInstanceIdentityToken {
signature: string
document: string
}

export interface WorkspaceAgentAuthenticateResponse {
session_token: string
}

export interface WorkspaceBuild {
name: string
job: ProvisionerJob
}

export interface WorkspaceResource {
type: string
name: string
}

export interface WorkspaceAgent {
status: WorkspaceAgentStatus
name: string
instance_id: string
architecture: string
operating_system: string
startup_script: string
}

export interface WorkspaceAgentResourceMetadata {
memory_total: uint64
disk_total: uint64
cpu_cores: uint64
cpu_model: string
cpu_mhz: float64
}

export interface WorkspaceAgentInstanceMetadata {
jail_orchestrator: string
operating_system: string
platform: string
platform_family: string
kernel_version: string
kernel_architecture: string
cloud: string
jail: string
vnc: bool
}

export interface Workspace {
template_name: string
latest_build: WorkspaceBuild
outdated: bool
name: string
autostart_schedule: string
autostop_schedule: string
}

export interface CreateWorkspaceBuildRequest {
dry_run: bool
}

export interface UpdateWorkspaceAutostartRequest {
schedule: string
}

export interface UpdateWorkspaceAutostopRequest {
schedule: string
}