-
Notifications
You must be signed in to change notification settings - Fork 0
<feature>[rest]: add go-sdk generator for zsv #3401
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
base: zsv_5.0.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| // Copyright (c) ZStack.io, Inc. | ||
|
|
||
| package param | ||
|
|
||
| import "time" | ||
|
|
||
| var _ = time.Now() // avoid unused import | ||
|
|
||
| type DeleteMode string | ||
|
|
||
| const ( | ||
| DeleteModePermissive DeleteMode = "Permissive" | ||
| DeleteModeEnforcing DeleteMode = "Enforcing" | ||
| ) | ||
|
|
||
| type BaseParam struct { | ||
| SystemTags []string `json:"systemTags,omitempty"` // System tags | ||
| UserTags []string `json:"userTags,omitempty"` // User tags | ||
| RequestIp string `json:"requestIp,omitempty"` // Request IP | ||
| } | ||
|
|
||
| type HqlParam struct { | ||
| OperationName string `json:"operationName"` // Request name | ||
| Query string `json:"query"` // Query statement | ||
| Variables Variables `json:"variables"` // Parameters for the statement | ||
| } | ||
|
|
||
| type Variables struct { | ||
| Conditions []Condition `json:"conditions"` // Conditions | ||
| ExtraConditions []Condition `json:"extraConditions"` // Extra conditions | ||
| Input map[string]interface{} `json:"input"` // Input parameters | ||
| PageVar `json:",inline,omitempty"` | ||
| Type string `json:"type"` // Type | ||
| } | ||
|
|
||
| type Condition struct { | ||
| Key string `json:"key"` // Key | ||
| Op string `json:"op"` // Operator | ||
| Value string `json:"value"` // Value | ||
| } | ||
|
|
||
| type PageVar struct { | ||
| Start int `json:"start,omitempty"` // Start page | ||
| Limit int `json:"limit,omitempty"` // Limit per page | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| // Copyright (c) ZStack.io, Inc. | ||
|
|
||
| package param | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
| "net/url" | ||
| "reflect" | ||
| "strings" | ||
|
|
||
| "github.com/fatih/structs" | ||
| ) | ||
|
|
||
| type QueryParam struct { | ||
| url.Values | ||
| } | ||
|
|
||
| func NewQueryParam() QueryParam { | ||
| return QueryParam{ | ||
| Values: make(url.Values), | ||
| } | ||
| } | ||
|
|
||
| // AddQ adds a query condition, similar to a MySQL database query. | ||
| // Omitting this field will return all records, with the number of returned records limited by the 'limit' field. | ||
| func (params *QueryParam) AddQ(q string) *QueryParam { | ||
| if params.Get("q") == "" { | ||
| params.Set("q", q) | ||
| } else { | ||
| params.Add("q", q) | ||
| } | ||
| return params | ||
| } | ||
|
|
||
| // Limit sets the maximum number of records to return, similar to MySQL's 'limit'. Default value is 1000. | ||
| func (params *QueryParam) Limit(limit int) *QueryParam { | ||
| params.Set("limit", fmt.Sprintf("%d", limit)) | ||
| return params | ||
| } | ||
|
|
||
| // Start sets the starting position for the query, similar to MySQL's 'offset'. Used with 'limit' for pagination. | ||
| func (params *QueryParam) Start(start int) *QueryParam { | ||
| params.Set("start", fmt.Sprintf("%d", start)) | ||
| return params | ||
| } | ||
|
|
||
| // Count sets the query to return the count of records that match the query conditions, similar to MySQL's 'count()' function. | ||
| func (params *QueryParam) Count(count bool) *QueryParam { | ||
| params.Set("count", fmt.Sprintf("%t", count)) | ||
| return params | ||
| } | ||
|
|
||
| // GroupBy groups the results by a specified field, similar to MySQL's 'group by' keyword. | ||
| func (params *QueryParam) GroupBy(groupBy string) *QueryParam { | ||
| params.Set("groupBy", groupBy) | ||
| return params | ||
| } | ||
|
|
||
| // ReplyWithCount, when set to true, includes the total count of records that match the query in the response. | ||
| func (params *QueryParam) ReplyWithCount(replyWithCount bool) *QueryParam { | ||
| params.Set("replyWithCount", fmt.Sprintf("%t", replyWithCount)) | ||
| return params | ||
| } | ||
|
|
||
| // FilterName sets a filter name, functionality is unknown from ZStack Java SDK (sdk-4.4.0.jar). | ||
| func (params *QueryParam) FilterName(filterName string) *QueryParam { | ||
| params.Set("filterName", filterName) | ||
| return params | ||
| } | ||
|
|
||
| // Sort sorts the results by a specified field, similar to MySQL's 'sort by' keyword. | ||
| // Use '+' for ascending order and '-' for descending order, followed by the field name. | ||
| func (params *QueryParam) Sort(sort string) *QueryParam { | ||
| params.Set("sort", sort) | ||
| return params | ||
| } | ||
|
|
||
| // Fields specifies the fields to return, similar to MySQL's 'select' fields functionality. | ||
| func (params *QueryParam) Fields(fields []string) *QueryParam { | ||
| params.Set("fields", strings.Join(fields, ",")) | ||
| return params | ||
| } | ||
|
|
||
| // ConvertStruct2UrlValues converts a struct to url.Values. | ||
| func ConvertStruct2UrlValues(param interface{}) (url.Values, error) { | ||
| if reflect.Ptr != reflect.TypeOf(param).Kind() { | ||
| return nil, errors.New("model should be pointer kind") | ||
| } | ||
| result := url.Values{} | ||
| if param == nil || reflect.ValueOf(param).IsNil() { | ||
| return nil, errors.New("param is nil") | ||
| } | ||
|
|
||
| s := structs.New(param) | ||
| s.TagName = "json" | ||
| mappedOpts := s.Map() | ||
| for k, v := range mappedOpts { | ||
| result.Set(k, fmt.Sprintf("%v", v)) | ||
| } | ||
| return result, nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,228 @@ | ||
| // Copyright (c) ZStack.io, Inc. | ||
|
|
||
| package client | ||
|
|
||
| import ( | ||
| "context" | ||
| "crypto/sha512" | ||
| "fmt" | ||
| "net/http" | ||
| "net/url" | ||
|
|
||
| "github.com/kataras/golog" | ||
| "github.com/zstackio/zsphere-sdk-go-v2/pkg/errors" | ||
| "github.com/zstackio/zsphere-sdk-go-v2/pkg/param" | ||
| "github.com/zstackio/zsphere-sdk-go-v2/pkg/view" | ||
| ) | ||
|
|
||
| type ZSClient struct { | ||
| *ZSHttpClient | ||
| } | ||
|
|
||
| func NewZSClient(config *ZSConfig) *ZSClient { | ||
| return &ZSClient{ | ||
| ZSHttpClient: NewZSHttpClient(config), | ||
| } | ||
| } | ||
|
|
||
| func (cli *ZSClient) Login(ctx context.Context) (*view.SessionInventoryView, error) { | ||
| if cli.authType != AuthTypeAccountUser && cli.authType != AuthTypeAccount { | ||
| return nil, errors.ErrNotSupported | ||
| } | ||
|
|
||
| var sessionView *view.SessionInventoryView | ||
| var err error | ||
| if cli.authType == AuthTypeAccountUser { | ||
| sessionView, err = cli.logInByAccountUser(ctx) | ||
| } else { | ||
| sessionView, err = cli.logInByAccount(ctx) | ||
| } | ||
|
|
||
| if err != nil { | ||
| golog.Errorf("ZSClient.Login error:%v", err) | ||
| return nil, err | ||
| } | ||
|
|
||
| cli.LoadSession(sessionView.Uuid) | ||
| return sessionView, nil | ||
| } | ||
|
|
||
| func (cli *ZSClient) logInByAccountUser(ctx context.Context) (*view.SessionInventoryView, error) { | ||
| if cli.authType != AuthTypeAccountUser { | ||
| return nil, errors.ErrNotSupported | ||
| } | ||
|
|
||
| if len(cli.accountName) == 0 || len(cli.accountUserName) == 0 || len(cli.password) == 0 { | ||
| return nil, errors.ErrParameter | ||
| } | ||
|
|
||
| params := param.LogInByUserParam{ | ||
| LogInByUser: param.LogInByUserDetailParam{ | ||
| AccountName: cli.accountName, | ||
| UserName: cli.accountUserName, | ||
| Password: fmt.Sprintf("%x", sha512.Sum512([]byte(cli.password))), | ||
| }, | ||
| } | ||
| sessionView := view.SessionInventoryView{} | ||
| err := cli.Put(ctx, "v1/accounts/users/login", "", params, &sessionView) | ||
| if err != nil { | ||
| golog.Errorf("ZSClient.logInByAccountUser Account[%s] User[%s] error:%v", | ||
| cli.accountName, cli.accountUserName, err) | ||
| return nil, err | ||
| } | ||
|
|
||
| return &sessionView, nil | ||
| } | ||
|
|
||
| func (cli *ZSClient) logInByAccount(ctx context.Context) (*view.SessionInventoryView, error) { | ||
| if cli.authType != AuthTypeAccount { | ||
| return nil, errors.ErrNotSupported | ||
| } | ||
|
|
||
| if len(cli.accountName) == 0 || len(cli.password) == 0 { | ||
| return nil, errors.ErrParameter | ||
| } | ||
|
|
||
| params := param.LoginByAccountParam{ | ||
| LoginByAccount: param.LoginByAccountDetailParam{ | ||
| AccountName: cli.accountName, | ||
| Password: fmt.Sprintf("%x", sha512.Sum512([]byte(cli.password))), | ||
| }, | ||
| } | ||
| sessionView := view.SessionInventoryView{} | ||
| err := cli.Put(ctx, "v1/accounts/login", "", params, &sessionView) | ||
| if err != nil { | ||
| golog.Errorf("ZSClient.logInByAccount Account[%s] error:%v", cli.accountName, err) | ||
| return nil, err | ||
| } | ||
|
|
||
| return &sessionView, nil | ||
| } | ||
|
|
||
| func (cli *ZSClient) ValidateSession(ctx context.Context) (map[string]bool, error) { | ||
| if cli.authType != AuthTypeAccountUser && cli.authType != AuthTypeAccount { | ||
| return nil, errors.ErrNotSupported | ||
| } | ||
|
|
||
| if len(cli.sessionId) == 0 { | ||
| return nil, errors.ErrNotSupported | ||
| } | ||
|
|
||
| return cli.ValidateSessionId(ctx, cli.sessionId) | ||
| } | ||
|
|
||
| func (cli *ZSClient) ValidateSessionId(ctx context.Context, sessionId string) (map[string]bool, error) { | ||
| validSession := make(map[string]bool) | ||
| err := cli.GetWithSpec(ctx, "v1/accounts/sessions", sessionId, "valid", "", nil, &validSession) | ||
| if err != nil { | ||
| golog.Errorf("ZSClient.ValidateSession sessionId[%s] error:%v", sessionId, err) | ||
| return nil, err | ||
| } | ||
|
|
||
| golog.Debugf("ZSClient.ValidateSession sessionId[%s]:%v", sessionId, validSession) | ||
| return validSession, nil | ||
| } | ||
|
|
||
| func (cli *ZSClient) Logout(ctx context.Context) error { | ||
| if cli.authType != AuthTypeAccountUser && cli.authType != AuthTypeAccount { | ||
| return errors.ErrNotSupported | ||
| } | ||
|
|
||
| if len(cli.sessionId) == 0 { | ||
| return errors.ErrNotSupported | ||
| } | ||
|
|
||
| err := cli.Delete(ctx, "v1/accounts/sessions", cli.sessionId, "") | ||
| if err != nil { | ||
| golog.Errorf("ZSClient.Logout sessionId[%s] error:%v", cli.sessionId, err) | ||
| return err | ||
| } | ||
|
|
||
| cli.unloadSession() | ||
| return nil | ||
| } | ||
|
|
||
| func (cli *ZSClient) WebLogin(ctx context.Context) (*view.WebUISessionView, error) { | ||
| if cli.authType != AuthTypeAccountUser && cli.authType != AuthTypeAccount { | ||
| return nil, errors.ErrNotSupported | ||
| } | ||
|
|
||
| var operationName, username, loginType, query string | ||
| var input map[string]interface{} | ||
| if cli.authType == AuthTypeAccount { | ||
| operationName, username, loginType = "loginByAccount", cli.accountName, "iam1" | ||
| input = map[string]interface{}{ | ||
| "accountName": cli.accountName, | ||
| "password": fmt.Sprintf("%x", sha512.Sum512([]byte(cli.password))), | ||
| } | ||
| query = `mutation loginByAccount($input:LoginByAccountInput!) { | ||
| loginByAccount(input: $input) { | ||
| sessionId, | ||
| accountUuid, | ||
| userUuid, | ||
| currentIdentity | ||
| } | ||
| }` | ||
| } else { | ||
| operationName, username, loginType = "loginIAM2VirtualID", cli.accountUserName, "iam2" | ||
| input = map[string]interface{}{ | ||
| "name": cli.accountUserName, | ||
| "password": fmt.Sprintf("%x", sha512.Sum512([]byte(cli.password))), | ||
| } | ||
| query = `mutation loginIAM2VirtualID($input:LoginIAM2VirtualIDInput!) { | ||
| loginIAM2VirtualID(input: $input) { | ||
| sessionId, | ||
| accountUuid, | ||
| userUuid, | ||
| currentIdentity | ||
| } | ||
| }` | ||
| } | ||
|
|
||
| result := new(view.WebUISessionView) | ||
| params := param.HqlParam{ | ||
| OperationName: operationName, | ||
| Query: query, | ||
| Variables: param.Variables{ | ||
| Input: input, | ||
| }, | ||
| } | ||
| respHeader, err := cli.hql(ctx, params, result, responseKeyData, operationName) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| result.UserName = username | ||
| result.LoginType = loginType | ||
| result.ZSVersion = respHeader.Get("Zs-Version") | ||
| return result, nil | ||
| } | ||
|
|
||
| func (cli *ZSClient) hql(ctx context.Context, params param.HqlParam, retVal interface{}, unMarshalKeys ...string) (http.Header, error) { | ||
| urlStr := fmt.Sprintf("http://%s:%d/graphql", cli.hostname, WebZStackPort) | ||
| _, respHeader, resp, err := cli.httpPost(ctx, urlStr, jsonMarshal(params), false) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| if retVal == nil { | ||
| return nil, nil | ||
| } | ||
|
|
||
| return respHeader, resp.Unmarshal(retVal, unMarshalKeys...) | ||
| } | ||
|
|
||
| func (cli *ZSClient) Zql(ctx context.Context, querySt string, retVal interface{}, unMarshalKeys ...string) (http.Header, error) { | ||
| encodedQuery := url.QueryEscape(querySt) | ||
| baseUrl := cli.getRequestURL("v1/zql") | ||
| urlStr := fmt.Sprintf("%s?zql=%s", baseUrl, encodedQuery) | ||
| _, respHeader, resp, err := cli.httpGet(ctx, urlStr, false) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| if retVal == nil { | ||
| return nil, nil | ||
| } | ||
|
|
||
| return respHeader, resp.Unmarshal(retVal, unMarshalKeys...) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 124
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 1318
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 177
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 128
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 8090
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
rg "type ZSHttpClient" --type goRepository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
find ./rest/src/main/resources/scripts/templates -type f | head -20Repository: MatheMatrix/zstack
Length of output: 475
🏁 Script executed:
rg "WebZStackPort" --type go -B 2 -A 2Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 2954
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 124
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 44
🏁 Script executed:
Repository: MatheMatrix/zstack
Length of output: 44
GraphQL 登录请求被硬编码为明文 HTTP,无法支持 HTTPS
第 201 行将 GraphQL 端点 URL 硬编码为
http://%s:%d/graphql,而同文件中的Zql方法(第 216 行)使用cli.getRequestURL()动态构造 URL。由于WebLogin调用hql执行登录认证,登录链路应该遵循服务配置支持 HTTPS,即便密码已被 SHA512 哈希处理。建议将hql改为使用类似getRequestURL的灵活方式构造 URL。🤖 Prompt for AI Agents