Skip to content

Commit

Permalink
extends ts gen cmd args: url(base url) and body(custom http request b…
Browse files Browse the repository at this point in the history
…ody).
  • Loading branch information
chaosannals authored and kevwan committed Feb 9, 2025
1 parent f747585 commit 7562da1
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 81 deletions.
2 changes: 2 additions & 0 deletions tools/goctl/api/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ func init() {
tsCmdFlags.StringVar(&tsgen.VarStringAPI, "api")
tsCmdFlags.StringVar(&tsgen.VarStringCaller, "caller")
tsCmdFlags.BoolVar(&tsgen.VarBoolUnWrap, "unwrap")
tsCmdFlags.StringVar(&tsgen.VarStringUrlPrefix, "url")
tsCmdFlags.BoolVar(&tsgen.VarBoolCustomBody, "body")

validateCmdFlags.StringVar(&validate.VarStringAPI, "api")

Expand Down
4 changes: 4 additions & 0 deletions tools/goctl/api/tsgen/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ var (
VarStringCaller string
// VarBoolUnWrap describes whether wrap or not.
VarBoolUnWrap bool
// VarStringUrlPrefix request url prefix
VarStringUrlPrefix string
// VarBoolCustomBody request custom body
VarBoolCustomBody bool
)

// TsCommand provides the entry to generate typescript codes
Expand Down
36 changes: 30 additions & 6 deletions tools/goctl/api/tsgen/genpacket.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ func genAPI(api *spec.ApiSpec, caller string) (string, error) {
if len(comment) > 0 {
fmt.Fprintf(&builder, "%s\n", comment)
}
fmt.Fprintf(&builder, "export function %s(%s) {\n", handler, paramsForRoute(route))
genericsType := ""
if VarBoolCustomBody {
genericsType = "<T>"
}
fmt.Fprintf(&builder, "export function %s%s(%s) {\n", handler, genericsType, paramsForRoute(route))
writeIndent(&builder, 1)
responseGeneric := "<null>"
if len(route.ResponseTypeName()) > 0 {
Expand All @@ -101,6 +105,9 @@ func genAPI(api *spec.ApiSpec, caller string) (string, error) {

func paramsForRoute(route spec.Route) string {
if route.RequestType == nil {
if VarBoolCustomBody {
return "body?: T"
}
return ""
}
hasParams := pathHasParams(route)
Expand Down Expand Up @@ -141,6 +148,10 @@ func paramsForRoute(route spec.Route) string {
}
}
}

if VarBoolCustomBody {
params = append(params, "body?: T")
}
return strings.Join(params, ", ")
}

Expand Down Expand Up @@ -173,14 +184,27 @@ func callParamsForRoute(route spec.Route, group spec.Group) string {
var params = []string{pathForRoute(route, group)}
if hasParams {
params = append(params, "params")
} else {
params = append(params, "null")
}

configParams := []string{}

if hasBody {
params = append(params, "req")
if VarBoolCustomBody {
configParams = append(configParams, "body: JSON.stringify(body ?? req)")
} else {
configParams = append(configParams, "body: JSON.stringify(req)")
}
} else if VarBoolCustomBody {
configParams = append(configParams, "body: body ? JSON.stringify(body): null")
}
if hasHeader {
params = append(params, "headers")
configParams = append(configParams, "headers: headers")
}

params = append(params, fmt.Sprintf("{%s}", strings.Join(configParams, ", ")))

return strings.Join(params, ", ")
}

Expand All @@ -198,12 +222,12 @@ func pathForRoute(route spec.Route, group spec.Group) string {
routePath = strings.Join(pathSlice, "/")
}
if len(prefix) == 0 {
return "`" + routePath + "`"
return "`" + VarStringUrlPrefix + routePath + "`"
}

prefix = strings.TrimPrefix(prefix, `"`)
prefix = strings.TrimSuffix(prefix, `"`)
return fmt.Sprintf("`%s/%s`", prefix, strings.TrimPrefix(routePath, "/"))
return fmt.Sprintf("`%s%s/%s`", VarStringUrlPrefix, prefix, strings.TrimPrefix(routePath, "/"))
}

func pathHasParams(route spec.Route) bool {
Expand All @@ -212,7 +236,7 @@ func pathHasParams(route spec.Route) bool {
return false
}

return len(ds.Members) != len(ds.GetBodyMembers())
return len(ds.Members) != (len(ds.GetBodyMembers()) + len(ds.GetTagMembers(headerTagKey)) + len(ds.GetTagMembers(pathTagKey)))
}

func hasRequestBody(route spec.Route) bool {
Expand Down
5 changes: 0 additions & 5 deletions tools/goctl/api/tsgen/genrequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
_ "embed"
"os"
"path/filepath"

"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
)

//go:embed request.ts
Expand All @@ -18,9 +16,6 @@ func genRequest(dir string) error {
}

filename := filepath.Join(abs, "gocliRequest.ts")
if pathx.FileExists(filename) {
return nil
}

return os.WriteFile(filename, []byte(requestTemplate), 0644)
}
140 changes: 77 additions & 63 deletions tools/goctl/api/tsgen/request.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
export type Method =
| 'get'
| 'GET'
| 'delete'
| 'DELETE'
| 'head'
| 'HEAD'
| 'options'
| 'OPTIONS'
| 'post'
| 'POST'
| 'put'
| 'PUT'
| 'patch'
| 'PATCH';
| "get"
| "GET"
| "delete"
| "DELETE"
| "head"
| "HEAD"
| "options"
| "OPTIONS"
| "post"
| "POST"
| "put"
| "PUT"
| "patch"
| "PATCH";

export type QueryParams = {
[key: string | symbol | number]: string|number;
} | null;

/**
* Parse route parameters for responseType
Expand All @@ -24,23 +28,23 @@ export function parseParams(url: string): Array<string> {
if (!ps) {
return [];
}
return ps.map((k) => k.replace(/:/, ''));
return ps.map((k) => k.replace(/:/, ""));
}

/**
* Generate url and parameters
* @param url
* @param params
*/
export function genUrl(url: string, params: any) {
export function genUrl(url: string, params: QueryParams) {
if (!params) {
return url;
}

const ps = parseParams(url);
ps.forEach((k) => {
const reg = new RegExp(`:${k}`);
url = url.replace(reg, params[k]);
url = url.replace(reg, params[k].toString());
});

const path: Array<string> = [];
Expand All @@ -50,77 +54,87 @@ export function genUrl(url: string, params: any) {
}
}

return url + (path.length > 0 ? `?${path.join('&')}` : '');
return url + (path.length > 0 ? `?${path.join("&")}` : "");
}

export async function request({
method,
url,
data,
config = {}
}: {
method: Method;
url: string;
data?: unknown;
config?: unknown;
}) {
export async function request(
method: Method,
url: string,
config?: RequestInit
) {
if (config?.body && /get|head/i.test(method)) {
throw new Error(
"Request with GET/HEAD method cannot have body. *.api service use other method, example: POST or PUT."
);
}
const response = await fetch(url, {
method: method.toLocaleUpperCase(),
credentials: 'include',
credentials: "include",
...config,
headers: {
'Content-Type': 'application/json'
"Content-Type": "application/json",
...config?.headers,
},
body: data ? JSON.stringify(data) : undefined,
// @ts-ignore
...config
});

return response.json();
if (response.headers.get('Content-Type') == 'application/json') {
return response.json();
} else {
return response.text();
}
}

function api<T>(
method: Method = 'get',
method: Method = "get",
url: string,
req: any,
config?: unknown
params?: QueryParams,
config?: RequestInit
): Promise<T> {
if (url.match(/:/) || method.match(/get|delete/i)) {
url = genUrl(url, req.params || req.forms);
if (params) {
url = genUrl(url, params);
}
method = method.toLocaleLowerCase() as Method;

switch (method) {
case 'get':
return request({method: 'get', url, data: req, config});
case 'delete':
return request({method: 'delete', url, data: req, config});
case 'put':
return request({method: 'put', url, data: req, config});
case 'post':
return request({method: 'post', url, data: req, config});
case 'patch':
return request({method: 'patch', url, data: req, config});
case "get":
return request("get", url, config);
case "delete":
return request("delete", url, config);
case "put":
return request("put", url, config);
case "post":
return request("post", url, config);
case "patch":
return request("patch", url, config);
default:
return request({method: 'post', url, data: req, config});
return request("post", url, config);
}
}

export const webapi = {
get<T>(url: string, req: unknown, config?: unknown): Promise<T> {
return api<T>('get', url, req, config);
get<T>(url: string, params?: QueryParams, config?: RequestInit): Promise<T> {
return api<T>("get", url, params, config);
},
delete<T>(url: string, req: unknown, config?: unknown): Promise<T> {
return api<T>('delete', url, req, config);
delete<T>(
url: string,
params?: QueryParams,
config?: RequestInit
): Promise<T> {
return api<T>("delete", url, params, config);
},
put<T>(url: string, req: unknown, config?: unknown): Promise<T> {
return api<T>('put', url, req, config);
put<T>(url: string, params?: QueryParams, config?: RequestInit): Promise<T> {
return api<T>("put", url, params, config);
},
post<T>(url: string, req: unknown, config?: unknown): Promise<T> {
return api<T>('post', url, req, config);
post<T>(url: string, params?: QueryParams, config?: RequestInit): Promise<T> {
return api<T>("post", url, params, config);
},
patch<T>(
url: string,
params?: QueryParams,
config?: RequestInit
): Promise<T> {
return api<T>("patch", url, params, config);
},
patch<T>(url: string, req: unknown, config?: unknown): Promise<T> {
return api<T>('patch', url, req, config);
}
};

export default webapi
export default webapi;
30 changes: 23 additions & 7 deletions tools/goctl/api/tsgen/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"strings"

"github.com/zeromicro/go-zero/core/stringx"
"github.com/zeromicro/go-zero/tools/goctl/api/spec"
apiutil "github.com/zeromicro/go-zero/tools/goctl/api/util"
"github.com/zeromicro/go-zero/tools/goctl/util"
Expand All @@ -18,6 +19,16 @@ const (
headerTagKey = "header"
)

func IsOptionalOrOmitEmpty(m spec.Member) bool {
tag := m.Tags()
for _, item := range tag {
if stringx.Contains(item.Options, "optional") || stringx.Contains(item.Options, "omitempty") {
return true
}
}
return false
}

func writeProperty(writer io.Writer, member spec.Member, indent int) error {
writeIndent(writer, indent)
ty, err := genTsType(member, indent)
Expand All @@ -26,13 +37,16 @@ func writeProperty(writer io.Writer, member spec.Member, indent int) error {
}

optionalTag := ""
if member.IsOptional() || member.IsOmitEmpty() {
if IsOptionalOrOmitEmpty(member) {
optionalTag = "?"
}
name, err := member.GetPropertyName()
if err != nil {
return err
}
if strings.Contains(name, "-") {
name = fmt.Sprintf("\"%s\"", name)
}

comment := member.GetComment()
if len(comment) > 0 {
Expand Down Expand Up @@ -150,7 +164,7 @@ func primitiveType(tp string) (string, bool) {
}

func writeType(writer io.Writer, tp spec.Type) error {
fmt.Fprintf(writer, "export interface %s {\n", util.Title(tp.Name()))
fmt.Fprintf(writer, "export type %s = {\n", util.Title(tp.Name()))
if err := writeMembers(writer, tp, false, 1); err != nil {
return err
}
Expand All @@ -170,14 +184,16 @@ func genParamsTypesIfNeed(writer io.Writer, tp spec.Type) error {
return nil
}

fmt.Fprintf(writer, "export interface %sParams {\n", util.Title(tp.Name()))
if err := writeTagMembers(writer, tp, formTagKey); err != nil {
return err
if len(definedType.GetTagMembers(formTagKey)) > 0 {
fmt.Fprintf(writer, "export type %sParams = {\n", util.Title(tp.Name()))
if err := writeTagMembers(writer, tp, formTagKey); err != nil {
return err
}
fmt.Fprintf(writer, "}\n")
}
fmt.Fprintf(writer, "}\n")

if len(definedType.GetTagMembers(headerTagKey)) > 0 {
fmt.Fprintf(writer, "export interface %sHeaders {\n", util.Title(tp.Name()))
fmt.Fprintf(writer, "export type %sHeaders = {\n", util.Title(tp.Name()))
if err := writeTagMembers(writer, tp, headerTagKey); err != nil {
return err
}
Expand Down

0 comments on commit 7562da1

Please sign in to comment.