private/apigen: Fix TS generator to apply naming conventions

Fix the TypeScript generator to generate code using the common
TypeScript conventions.

Closes https://github.com/storj/storj/issues/6360

Change-Id: I244896feea389670eca0df95d3ac58e25d600e14
This commit is contained in:
Ivan Fraixedes 2023-10-03 16:30:01 +02:00
parent 71c9547de5
commit 47f344927d
No known key found for this signature in database
GPG Key ID: FB6101AFB5CB5AD5
6 changed files with 41 additions and 28 deletions

View File

@ -10,9 +10,8 @@ import (
"regexp"
"sort"
"strings"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"unicode"
"unicode/utf8"
"storj.io/storj/private/api"
)
@ -122,17 +121,25 @@ func isNillableType(t reflect.Type) bool {
}
// compoundTypeName create a name composed with base and parts, by joining base as it's and
// capitalizing each part.
// capitalizing each part. base is not altered.
func compoundTypeName(base string, parts ...string) string {
caser := cases.Title(language.Und)
titled := make([]string, len(parts))
for i := 0; i < len(parts); i++ {
titled[i] = caser.String(parts[i])
titled[i] = capitalize(parts[i])
}
return base + strings.Join(titled, "")
}
func capitalize(s string) string {
r, size := utf8.DecodeRuneInString(s)
if size <= 0 {
return s
}
return string(unicode.ToTitle(r)) + s[size:]
}
type typeAndName struct {
Type reflect.Type
Name string

View File

@ -273,7 +273,7 @@ func (p Param) namedType(ep Endpoint, where string) reflect.Type {
if p.Type.Name() == "" {
return typeCustomName{
Type: p.Type,
name: compoundTypeName(ep.GoName, where, "param", p.Name),
name: compoundTypeName(ep.TypeScriptName, where, "param", p.Name),
}
}

View File

@ -22,43 +22,43 @@ export class Version {
number: number;
}
export class getResponseItem {
export class GetResponseItem {
id: UUID;
path: string;
date: Time;
metadata: Metadata;
last_retrievals?: getResponseItemLastretrievals;
last_retrievals?: GetResponseItemLastRetrievals;
}
export class getResponseItemLastretrievalsItem {
export class GetResponseItemLastRetrievalsItem {
user: string;
when: Time;
}
export class updateContentRequest {
export class UpdateContentRequest {
content: string;
}
export class updateContentResponse {
export class UpdateContentResponse {
id: UUID;
date: Time;
pathParam: string;
body: string;
}
export type getResponse = Array<getResponseItem>
export type GetResponse = Array<GetResponseItem>
export type getResponseItemLastretrievals = Array<getResponseItemLastretrievalsItem>
export type GetResponseItemLastRetrievals = Array<GetResponseItemLastRetrievalsItem>
export class docsHttpApiV0 {
export class DocsHttpApiV0 {
private readonly http: HttpClient = new HttpClient();
private readonly ROOT_PATH: string = '/api/v0/docs';
public async get(): Promise<getResponse> {
public async get(): Promise<GetResponse> {
const fullPath = `${this.ROOT_PATH}/`;
const response = await this.http.get(fullPath);
if (response.ok) {
return response.json().then((body) => body as getResponse);
return response.json().then((body) => body as GetResponse);
}
const err = await response.json();
throw new Error(err.error);
@ -94,14 +94,14 @@ export class docsHttpApiV0 {
throw new Error(err.error);
}
public async updateContent(request: updateContentRequest, path: string, id: UUID, date: Time): Promise<updateContentResponse> {
public async updateContent(request: UpdateContentRequest, path: string, id: UUID, date: Time): Promise<UpdateContentResponse> {
const u = new URL(`${this.ROOT_PATH}/${path}`);
u.searchParams.set('id', id);
u.searchParams.set('date', date);
const fullPath = u.toString();
const response = await this.http.post(fullPath, JSON.stringify(request));
if (response.ok) {
return response.json().then((body) => body as updateContentResponse);
return response.json().then((body) => body as UpdateContentResponse);
}
const err = await response.json();
throw new Error(err.error);

View File

@ -85,7 +85,7 @@ func (f *tsGenFile) registerTypes() {
}
func (f *tsGenFile) createAPIClient(group *EndpointGroup) {
f.pf("\nexport class %sHttpApi%s {", group.Prefix, strings.ToUpper(f.api.Version))
f.pf("\nexport class %sHttpApi%s {", capitalize(group.Prefix), strings.ToUpper(f.api.Version))
f.pf("\tprivate readonly http: HttpClient = new HttpClient();")
f.pf("\tprivate readonly ROOT_PATH: string = '%s/%s';", f.api.endpointBasePath(), group.Prefix)
for _, method := range group.endpoints {

View File

@ -156,7 +156,8 @@ func (types *Types) GenerateTypescriptDefinitions() string {
for _, t := range allStructs {
func() {
pf("\nexport class %s {", t.Name)
name := capitalize(t.Name)
pf("\nexport class %s {", name)
defer pf("}")
for i := 0; i < t.Type.NumField(); i++ {
@ -165,7 +166,7 @@ func (types *Types) GenerateTypescriptDefinitions() string {
if len(attributes) == 0 || attributes[0] == "" {
pathParts := strings.Split(t.Type.PkgPath(), "/")
pkg := pathParts[len(pathParts)-1]
panic(fmt.Sprintf("(%s.%s).%s missing json declaration", pkg, t.Name, field.Name))
panic(fmt.Sprintf("(%s.%s).%s missing json declaration", pkg, name, field.Name))
}
jsonField := attributes[0]
@ -206,7 +207,12 @@ func (types *Types) GenerateTypescriptDefinitions() string {
if !ok {
panic("BUG: the element types of an Slice or Array isn't in the all types map")
}
pf("\nexport type %s = Array<%s>", t.Name, elemTypeName)
pf(
"\nexport type %s = Array<%s>",
TypescriptTypeName(
typeCustomName{Type: t.Type, name: t.Name}),
TypescriptTypeName(typeCustomName{Type: t.Type.Elem(), name: elemTypeName}),
)
}
return out.String()
@ -245,7 +251,7 @@ func TypescriptTypeName(t reflect.Type) string {
return TypescriptTypeName(t.Elem())
case reflect.Array, reflect.Slice:
if t.Name() != "" {
return t.Name()
return capitalize(t.Name())
}
// []byte ([]uint8) is marshaled as a base64 string
@ -266,7 +272,7 @@ func TypescriptTypeName(t reflect.Type) string {
case reflect.Bool:
return "boolean"
case reflect.Struct:
return t.Name()
return capitalize(t.Name())
default:
panic(fmt.Sprintf(`unhandled type. Type="%+v"`, t))
}

View File

@ -94,7 +94,7 @@ export class UpsertProjectInfo {
createdAt: Time;
}
export class projectsHttpApiV0 {
export class ProjectsHttpApiV0 {
private readonly http: HttpClient = new HttpClient();
private readonly ROOT_PATH: string = '/api/v0/projects';
@ -184,7 +184,7 @@ export class projectsHttpApiV0 {
}
}
export class apikeysHttpApiV0 {
export class ApikeysHttpApiV0 {
private readonly http: HttpClient = new HttpClient();
private readonly ROOT_PATH: string = '/api/v0/apikeys';
@ -209,7 +209,7 @@ export class apikeysHttpApiV0 {
}
}
export class usersHttpApiV0 {
export class UsersHttpApiV0 {
private readonly http: HttpClient = new HttpClient();
private readonly ROOT_PATH: string = '/api/v0/users';