4d823e8166
The code responsible for generating TypeScript classes has been separated from the rest of the TypeScript generation code so that other packages may take advantage of this functionality. References #5494 Change-Id: I97eabd430bd6a5f748eafaf8b1d783977e75e660
158 lines
3.9 KiB
Go
158 lines
3.9 KiB
Go
// Copyright (C) 2022 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package apigen
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/zeebo/errs"
|
|
)
|
|
|
|
// MustWriteTS writes generated TypeScript code into a file.
|
|
func (a *API) MustWriteTS(path string) {
|
|
f := newTSGenFile(path, a)
|
|
|
|
f.generateTS()
|
|
|
|
err := f.write()
|
|
if err != nil {
|
|
panic(errs.Wrap(err))
|
|
}
|
|
}
|
|
|
|
type tsGenFile struct {
|
|
result string
|
|
path string
|
|
api *API
|
|
types Types
|
|
}
|
|
|
|
func newTSGenFile(filepath string, api *API) *tsGenFile {
|
|
return &tsGenFile{
|
|
path: filepath,
|
|
api: api,
|
|
types: NewTypes(),
|
|
}
|
|
}
|
|
|
|
func (f *tsGenFile) pf(format string, a ...interface{}) {
|
|
f.result += fmt.Sprintf(format+"\n", a...)
|
|
}
|
|
|
|
func (f *tsGenFile) write() error {
|
|
content := strings.ReplaceAll(f.result, "\t", " ")
|
|
return os.WriteFile(f.path, []byte(content), 0644)
|
|
}
|
|
|
|
func (f *tsGenFile) generateTS() {
|
|
f.pf("// AUTOGENERATED BY private/apigen")
|
|
f.pf("// DO NOT EDIT.")
|
|
f.pf("")
|
|
f.pf("import { HttpClient } from '@/utils/httpClient';")
|
|
|
|
f.registerTypes()
|
|
f.result += f.types.GenerateTypescriptDefinitions()
|
|
|
|
for _, group := range f.api.EndpointGroups {
|
|
// Not sure if this is a good name
|
|
f.createAPIClient(group)
|
|
}
|
|
}
|
|
|
|
func (f *tsGenFile) registerTypes() {
|
|
for _, group := range f.api.EndpointGroups {
|
|
for _, method := range group.endpoints {
|
|
if method.Request != nil {
|
|
f.types.Register(reflect.TypeOf(method.Request))
|
|
}
|
|
if method.Response != nil {
|
|
f.types.Register(reflect.TypeOf(method.Response))
|
|
}
|
|
if len(method.QueryParams) > 0 {
|
|
for _, p := range method.QueryParams {
|
|
t := getElementaryType(p.Type)
|
|
f.types.Register(t)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (f *tsGenFile) createAPIClient(group *EndpointGroup) {
|
|
f.pf("\nexport class %sHttpApi%s {", group.Prefix, strings.ToUpper(f.api.Version))
|
|
f.pf("\tprivate readonly http: HttpClient = new HttpClient();")
|
|
f.pf("\tprivate readonly ROOT_PATH: string = '/api/%s/%s';", f.api.Version, group.Prefix)
|
|
for _, method := range group.endpoints {
|
|
f.pf("")
|
|
|
|
funcArgs, path := f.getArgsAndPath(method)
|
|
|
|
returnStmt := "return"
|
|
returnType := "void"
|
|
if method.Response != nil {
|
|
returnType = TypescriptTypeName(getElementaryType(reflect.TypeOf(method.Response)))
|
|
if v := reflect.ValueOf(method.Response); v.Kind() == reflect.Array || v.Kind() == reflect.Slice {
|
|
returnType = fmt.Sprintf("Array<%s>", returnType)
|
|
}
|
|
returnStmt += fmt.Sprintf(" response.json().then((body) => body as %s)", returnType)
|
|
}
|
|
returnStmt += ";"
|
|
|
|
f.pf("\tpublic async %s(%s): Promise<%s> {", method.RequestName, funcArgs, returnType)
|
|
f.pf("\t\tconst path = `%s`;", path)
|
|
|
|
if method.Request != nil {
|
|
f.pf("\t\tconst response = await this.http.%s(path, JSON.stringify(request));", strings.ToLower(method.Method))
|
|
} else {
|
|
f.pf("\t\tconst response = await this.http.%s(path);", strings.ToLower(method.Method))
|
|
}
|
|
|
|
f.pf("\t\tif (response.ok) {")
|
|
f.pf("\t\t\t%s", returnStmt)
|
|
f.pf("\t\t}")
|
|
f.pf("\t\tconst err = await response.json();")
|
|
f.pf("\t\tthrow new Error(err.error);")
|
|
f.pf("\t}")
|
|
}
|
|
f.pf("}")
|
|
}
|
|
|
|
func (f *tsGenFile) getArgsAndPath(method *fullEndpoint) (funcArgs, path string) {
|
|
// remove path parameter placeholders
|
|
path = method.Path
|
|
i := strings.Index(path, "{")
|
|
if i > -1 {
|
|
path = method.Path[:i]
|
|
}
|
|
path = "${this.ROOT_PATH}" + path
|
|
|
|
if method.Request != nil {
|
|
t := getElementaryType(reflect.TypeOf(method.Request))
|
|
funcArgs += fmt.Sprintf("request: %s, ", TypescriptTypeName(t))
|
|
}
|
|
|
|
for _, p := range method.PathParams {
|
|
funcArgs += fmt.Sprintf("%s: %s, ", p.Name, TypescriptTypeName(p.Type))
|
|
path += fmt.Sprintf("/${%s}", p.Name)
|
|
}
|
|
|
|
for i, p := range method.QueryParams {
|
|
if i == 0 {
|
|
path += "?"
|
|
} else {
|
|
path += "&"
|
|
}
|
|
|
|
funcArgs += fmt.Sprintf("%s: %s, ", p.Name, TypescriptTypeName(p.Type))
|
|
path += fmt.Sprintf("%s=${%s}", p.Name, p.Name)
|
|
}
|
|
|
|
path = strings.ReplaceAll(path, "//", "/")
|
|
|
|
return strings.Trim(funcArgs, ", "), path
|
|
}
|