Skip to content

Commit f34fae6

Browse files
When a reserved variable is set in a parameter, you need to determine whether it is also a query parameter required by the API. (#1185)
1 parent c88b08a commit f34fae6

File tree

10 files changed

+432
-9
lines changed

10 files changed

+432
-9
lines changed

cli/context.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,18 @@ func NewHelpFlag() *Flag {
3737

3838
// CLI Command Context
3939
type Context struct {
40-
help bool
41-
flags *FlagSet
42-
unknownFlags *FlagSet
43-
command *Command
44-
completion *Completion
45-
stdout io.Writer
46-
stderr io.Writer
40+
help bool
41+
flags *FlagSet
42+
unknownFlags *FlagSet
43+
command *Command
44+
completion *Completion
45+
stdout io.Writer
46+
stderr io.Writer
47+
inConfigureMode bool
48+
}
49+
50+
func (ctx *Context) InConfigureMode() bool {
51+
return ctx.inConfigureMode
4752
}
4853

4954
func NewCommandContext(stdout io.Writer, stderr io.Writer) *Context {
@@ -147,3 +152,7 @@ func (ctx *Context) detectFlagByShorthand(ch rune) (*Flag, error) {
147152
}
148153
return nil, fmt.Errorf("unknown flag -%s", string(ch))
149154
}
155+
156+
func (ctx *Context) SetInConfigureMode(mode bool) {
157+
ctx.inConfigureMode = mode
158+
}

cli/context_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,15 @@ func TestDetectFlagByShorthand(t *testing.T) {
128128
assert.Nil(t, f)
129129
assert.EqualError(t, err, "unknown flag -c")
130130
}
131+
132+
func TestSetInConfigureMode(t *testing.T) {
133+
w := new(bytes.Buffer)
134+
stderr := new(bytes.Buffer)
135+
ctx := NewCommandContext(w, stderr)
136+
137+
ctx.SetInConfigureMode(true)
138+
assert.True(t, ctx.InConfigureMode())
139+
140+
ctx.SetInConfigureMode(false)
141+
assert.False(t, ctx.InConfigureMode())
142+
}

config/configuration.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,10 @@ func LoadProfileWithContext(ctx *cli.Context) (profile Profile, err error) {
144144
}
145145

146146
// Load from flags
147-
profile.OverwriteWithFlags(ctx)
147+
if ctx.InConfigureMode() {
148+
// If not in configure mode, we will not overwrite the profile with flags
149+
profile.OverwriteWithFlags(ctx)
150+
}
148151
err = profile.Validate()
149152
return
150153
}

config/configuration_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ func TestLoadProfileWithContextWhenIGNORE_PROFILE(t *testing.T) {
284284
stdout := new(bytes.Buffer)
285285
stderr := new(bytes.Buffer)
286286
ctx := cli.NewCommandContext(stdout, stderr)
287+
ctx.SetInConfigureMode(true)
287288
AddFlags(ctx.Flags())
288289
ctx.Flags().Get("access-key-id").SetAssigned(true)
289290
ctx.Flags().Get("access-key-id").SetValue("test-ak-id")

main/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func Main(args []string) {
6363
ctx := cli.NewCommandContext(stdout, stderr)
6464
ctx.EnterCommand(rootCmd)
6565
ctx.SetCompletion(cli.ParseCompletionForShell())
66+
ctx.SetInConfigureMode(openapi.DetectInConfigureMode(ctx.Flags()))
6667

6768
rootCmd.AddSubCommand(config.NewConfigureCommand())
6869
rootCmd.AddSubCommand(lib.NewOssCommand())

meta/repository.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package meta
1515

1616
import (
1717
"encoding/json"
18+
"regexp"
1819
"sort"
1920
"strings"
2021

@@ -74,6 +75,36 @@ func (a *Repository) GetApi(productCode string, version string, apiName string)
7475
return result, true
7576
}
7677

78+
func (a *Repository) GetApiByPath(productCode string, version string, method string, path string) (Api, bool) {
79+
var result Api
80+
product, ok := a.GetProduct(productCode)
81+
if !ok {
82+
return result, false
83+
}
84+
// list all apis
85+
for _, apiName := range product.ApiNames {
86+
err := ReadJsonFrom(strings.ToLower(product.Code)+"/"+apiName+".json", &result)
87+
if err != nil {
88+
return result, false
89+
}
90+
// method not equal
91+
if result.Method != method {
92+
continue
93+
}
94+
// replace all [something] to *
95+
// example /permissions/users/[uid]/update to /permissions/users/*/update
96+
var pattern = ReplacePathPattern(result.PathPattern)
97+
// match path
98+
re := regexp.MustCompile("^" + pattern + "$")
99+
if re.MatchString(path) {
100+
result.Product = &product
101+
return result, true
102+
}
103+
}
104+
result = Api{}
105+
return result, false
106+
}
107+
77108
//go:embed versions.json
78109
var versions []byte
79110

@@ -118,3 +149,8 @@ func (a *Repository) GetStyle(productCode, version string) (string, bool) {
118149

119150
return "", false
120151
}
152+
153+
func ReplacePathPattern(pattern string) string {
154+
re := regexp.MustCompile(`\[[^\]]+\]`)
155+
return re.ReplaceAllString(pattern, "[0-9a-zA-Z_\\-\\.{}]+")
156+
}

meta/repository_test.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,117 @@ func TestGetStyle(t *testing.T) {
5050
_, ok = repository.GetStyle("invalid_product", "2016-11-11")
5151
assert.False(t, ok)
5252
}
53+
54+
func TestReplacePathPattern(t *testing.T) {
55+
type args struct {
56+
pattern string
57+
}
58+
tests := []struct {
59+
name string
60+
args args
61+
want string
62+
}{
63+
{
64+
name: "test1",
65+
args: args{
66+
pattern: "/permissions/users/[uid]/update",
67+
},
68+
want: "/permissions/users/[0-9a-zA-Z_\\-\\.{}]+/update",
69+
},
70+
{
71+
name: "test2",
72+
args: args{
73+
pattern: "/permissions/users/[uid]/update/[id]",
74+
},
75+
want: "/permissions/users/[0-9a-zA-Z_\\-\\.{}]+/update/[0-9a-zA-Z_\\-\\.{}]+",
76+
},
77+
}
78+
for _, tt := range tests {
79+
t.Run(tt.name, func(t *testing.T) {
80+
assert.Equalf(t, tt.want, ReplacePathPattern(tt.args.pattern), "ReplacePathPattern(%v)", tt.args.pattern)
81+
})
82+
}
83+
}
84+
85+
func TestRepository_GetApiByPath(t *testing.T) {
86+
type fields struct {
87+
Products []Product
88+
Names []string
89+
index map[string]Product
90+
}
91+
type args struct {
92+
productCode string
93+
version string
94+
method string
95+
path string
96+
}
97+
tests := []struct {
98+
name string
99+
fields fields
100+
args args
101+
want Api
102+
want1 bool
103+
}{
104+
{
105+
name: "test1",
106+
fields: fields{
107+
Products: []Product{
108+
{
109+
Code: "cs",
110+
ApiNames: []string{"UpdateUserPermissions"},
111+
},
112+
},
113+
Names: []string{"cs"},
114+
index: map[string]Product{
115+
"cs": {
116+
Code: "cs",
117+
ApiNames: []string{"UpdateUserPermissions"},
118+
},
119+
},
120+
},
121+
args: args{
122+
productCode: "cs",
123+
version: "2015-12-15",
124+
method: "POST",
125+
path: "/permissions/users/xx/update",
126+
},
127+
want1: true,
128+
},
129+
{
130+
name: "test2",
131+
fields: fields{
132+
Products: []Product{
133+
{
134+
Code: "cs",
135+
ApiNames: []string{"UpdateUserPermissions"},
136+
},
137+
},
138+
Names: []string{"cs"},
139+
index: map[string]Product{
140+
"cs": {
141+
Code: "cs",
142+
ApiNames: []string{"UpdateUserPermissions"},
143+
},
144+
},
145+
},
146+
args: args{
147+
productCode: "cs",
148+
version: "2015-12-15",
149+
method: "POST",
150+
path: "/permissions/users/xx/update2",
151+
},
152+
want1: false,
153+
},
154+
}
155+
for _, tt := range tests {
156+
t.Run(tt.name, func(t *testing.T) {
157+
a := &Repository{
158+
Products: tt.fields.Products,
159+
Names: tt.fields.Names,
160+
index: tt.fields.index,
161+
}
162+
_, got1 := a.GetApiByPath(tt.args.productCode, tt.args.version, tt.args.method, tt.args.path)
163+
assert.Equalf(t, tt.want1, got1, "GetApiByPath(%v, %v, %v, %v)", tt.args.productCode, tt.args.version, tt.args.method, tt.args.path)
164+
})
165+
}
166+
}

openapi/commando.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,72 @@ func (c *Commando) InitWithCommand(cmd *cli.Command) {
5252
cmd.AutoComplete = c.complete
5353
}
5454

55+
func DetectInConfigureMode(flags *cli.FlagSet) bool {
56+
_, modeExist := flags.GetValue(config.ModeFlagName)
57+
if !modeExist {
58+
return true
59+
}
60+
// if mode exist, check if other flags exist
61+
_, akExist := flags.GetValue(config.AccessKeyIdFlagName)
62+
if akExist {
63+
return true
64+
}
65+
_, skExist := flags.GetValue(config.AccessKeySecretFlagName)
66+
if skExist {
67+
return true
68+
}
69+
_, stsExist := flags.GetValue(config.StsTokenFlagName)
70+
if stsExist {
71+
return true
72+
}
73+
// RamRoleNameFlagName
74+
_, ramRoleNameExist := flags.GetValue(config.RamRoleNameFlagName)
75+
if ramRoleNameExist {
76+
return true
77+
}
78+
// RamRoleArnFlagName
79+
_, ramRoleArnExist := flags.GetValue(config.RamRoleArnFlagName)
80+
if ramRoleArnExist {
81+
return true
82+
}
83+
// RoleSessionNameFlagName
84+
_, roleSessionNameExist := flags.GetValue(config.RoleSessionNameFlagName)
85+
if roleSessionNameExist {
86+
return true
87+
}
88+
// PrivateKeyFlagName
89+
_, privateKeyExist := flags.GetValue(config.PrivateKeyFlagName)
90+
if privateKeyExist {
91+
return true
92+
}
93+
// KeyPairNameFlagName
94+
_, keyPairNameExist := flags.GetValue(config.KeyPairNameFlagName)
95+
if keyPairNameExist {
96+
return true
97+
}
98+
// OIDCProviderARNFlagName
99+
_, oidcProviderArnExist := flags.GetValue(config.OIDCProviderARNFlagName)
100+
if oidcProviderArnExist {
101+
return true
102+
}
103+
// OIDCTokenFileFlagName
104+
_, oidcTokenFileExist := flags.GetValue(config.OIDCTokenFileFlagName)
105+
if oidcTokenFileExist {
106+
return true
107+
}
108+
return false
109+
}
110+
55111
func (c *Commando) main(ctx *cli.Context, args []string) error {
56112
// aliyun
57113
if len(args) == 0 {
58114
c.printUsage(ctx)
59115
return nil
60116
}
61117

118+
// detect if in configure mode
119+
ctx.SetInConfigureMode(DetectInConfigureMode(ctx.Flags()))
120+
62121
// update current `Profile` with flags
63122
var err error
64123
c.profile, err = config.LoadProfileWithContext(ctx)
@@ -96,14 +155,27 @@ func (c *Commando) main(ctx *cli.Context, args []string) error {
96155
}
97156
if product.ApiStyle == "restful" {
98157
api, _ := c.library.GetApi(product.Code, product.Version, args[1])
158+
c.CheckApiParamWithBuildInArgs(ctx, api)
99159
ctx.Command().Name = args[1]
100160
return c.processInvoke(ctx, productName, api.Method, api.PathPattern)
161+
} else {
162+
// RPC need check API parameters too
163+
api, _ := c.library.GetApi(product.Code, product.Version, args[1])
164+
c.CheckApiParamWithBuildInArgs(ctx, api)
101165
}
102166

103167
return c.processInvoke(ctx, productName, args[1], "")
104168
} else if len(args) == 3 {
105169
// restful call
106170
// aliyun <productCode> {GET|PUT|POST|DELETE} <path> --
171+
product, _ := c.library.GetProduct(productName)
172+
api, find := c.library.GetApiByPath(product.Code, product.Version, args[1], args[2])
173+
if !find {
174+
// throw error, can not find api by path
175+
return cli.NewErrorWithTip(fmt.Errorf("can not find api by path %s", args[2]),
176+
"Please confirm if the API path exists")
177+
}
178+
c.CheckApiParamWithBuildInArgs(ctx, api)
107179
return c.processInvoke(ctx, productName, args[1], args[2])
108180
} else {
109181
return cli.NewErrorWithTip(fmt.Errorf("too many arguments"),
@@ -418,3 +490,20 @@ func (c *Commando) printUsage(ctx *cli.Context) {
418490
cmd.PrintSample(ctx)
419491
cmd.PrintTail(ctx)
420492
}
493+
494+
func (c *Commando) CheckApiParamWithBuildInArgs(ctx *cli.Context, api meta.Api) {
495+
for _, p := range api.Parameters {
496+
// 如果参数中包含了known参数,且 known参数已经被赋值,则将 known 参数拷贝给 unknown 参数
497+
if ep, ok := ctx.Flags().GetValue(p.Name); ok {
498+
if p.Position != "Query" {
499+
continue
500+
}
501+
var flagNew = &cli.Flag{
502+
Name: p.Name,
503+
}
504+
flagNew.SetValue(ep)
505+
flagNew.SetAssigned(true)
506+
ctx.UnknownFlags().Add(flagNew)
507+
}
508+
}
509+
}

0 commit comments

Comments
 (0)