diff --git a/CHANGELOG.md b/CHANGELOG.md index 041215979..07e23456f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,33 @@ ### Master +- Add flag `--filter` for filtering result, the value of the flag is [jmespath](http://jmespath.org/) format. +- Add flag `--format` to control the output format, the value can be json or table, the table is not csv format, just like below: + +```shell ++------------------------------------------------------+ +| DescribeRegions | ++------------------------------------------------------+ +| RequestId | A71F2A35-BA66-496F-8B3F-BC19F4268937 | +|+----------------------------------------------------+| +|| Regions || +|+----------------------------------------------------+| +||+--------------------------------------------------+|| +||| Region ||| +||+--------------------------------------------------+|| +||| LocalName | 华北 1 ||| +||| RegionEndpoint | ecs.aliyuncs.com ||| +||| RegionId | cn-qingdao ||| +||+--------------------------------------------------+|| +||+--------------------------------------------------+|| +||| Region ||| +||+--------------------------------------------------+|| +||| LocalName | 华北 2 ||| +||| RegionEndpoint | ecs.aliyuncs.com ||| +||| RegionId | cn-beijing ||| +||+--------------------------------------------------+|| +``` + - fix: help generating information bug - Upgrade the ossutil component to version 1.6.6 - Package manager switches to Go Modules diff --git a/humanFormat/fromJSONto.go b/humanFormat/fromJSONto.go new file mode 100644 index 000000000..e1cd72a65 --- /dev/null +++ b/humanFormat/fromJSONto.go @@ -0,0 +1,144 @@ +// Copyright 1999-2019 Alibaba Group Holding Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package format + +import ( + "encoding/json" + "fmt" + "reflect" + "sort" +) + +type tableEncoder struct { + table *Table + v interface{} + hasRow bool +} + +var funcList []func() +var backupFuncList []func() +var count int + +// FromJSONFull Convert from json to table format +func FromJSON(data []byte, table *Table) { + e := new(tableEncoder) + e.table = table + var v interface{} + err := json.Unmarshal(data, &v) + if err != nil { + panic(err) + } + e.v = v + funcList = append(funcList, e.marshal) + for { + if len(funcList) == 0 && len(backupFuncList) == 0 { + break + } + switch count % 2 { + case 0: + for _, v := range funcList { + v() + } + funcList = []func(){} + case 1: + for _, v := range backupFuncList { + v() + } + backupFuncList = []func(){} + } + count++ + } +} + +func (e *tableEncoder) marshal() { + e.regularJSONObject() + deleteEmptyTable(e.table) +} + +// deleteEmptyTable Delete empty table from bottom to top +func deleteEmptyTable(table *Table) { + if table.IsEmptyCell() && table.IsEmptySub() { + table.ParentTable().Remove(table) + } + if table.ParentTable() != nil { + deleteEmptyTable(table.ParentTable()) + } +} + +func (e *tableEncoder) regularJSONObject() { + switch reflect.ValueOf(e.v).Kind() { + case reflect.Map: + vMap := e.v.(map[string]interface{}) + var keySlice []string + for k := range vMap { + keySlice = append(keySlice, k) + } + sort.Strings(keySlice) + for _, k := range keySlice { + v := vMap[k] + reflectValue := reflect.ValueOf(v) + if reflectValue.String() == "" { + e.table.AddRow(fmt.Sprintf("%s\t", k)) + } + kind := reflectValue.Kind() + if kind != reflect.Map && kind != reflect.Slice { + e.table.AddRow(fmt.Sprintf("%s\t%v", k, v)) + e.hasRow = true + continue + } + subTable := e.table.AddNewTable(e.table.w).AddTitle(k) + subE := new(tableEncoder) + subE.table = subTable + subE.v = v + switch count % 2 { + case 0: + backupFuncList = append(backupFuncList, subE.marshal) + case 1: + funcList = append(funcList, subE.marshal) + } + } + case reflect.Slice: + if len(e.v.([]interface{})) == 0 { + return + } + var title string + if e.table.title != nil { + title = string(*e.table.title) + } else { + title = string(*e.table.ParentTable().title) + } + e.table.ParentTable().Remove(e.table) + + e.table = e.table.ParentTable() + + for _, v := range e.v.([]interface{}) { + kind := reflect.TypeOf(v).Kind() + if kind != reflect.Map && kind != reflect.Slice { + e.table.AddRow(fmt.Sprintf("%s\t%v", string(title), v)) + continue + } + subTable := e.table.AddNewTable(e.table.w).AddTitle(string(title)) + subE := new(tableEncoder) + subE.table = subTable + subE.v = v + switch count % 2 { + case 0: + backupFuncList = append(backupFuncList, subE.marshal) + case 1: + funcList = append(funcList, subE.marshal) + } + } + } +} diff --git a/humanFormat/table.go b/humanFormat/table.go new file mode 100644 index 000000000..dbb6e1c05 --- /dev/null +++ b/humanFormat/table.go @@ -0,0 +1,322 @@ +// Copyright 1999-2019 Alibaba Group Holding Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package format + +import ( + "io" + "strings" +) + +type title string +type row []string +type render interface { + flush(table *Table) string +} + +// Table Table structure +type Table struct { + BorderStyle + w io.Writer + width int + // Text and border spacing + padding int + parent *Table + subTable []*Table + cells []render + title *title + maxCellWidth map[int]int +} + +// The marker selects the boundary position +const ( + TopFlag uint = 1 << iota + BottomFlag + LeftFlag + RightFlag + TopLeftFlag + TopRightFlag + BottomLeftFlag + BottomRightFlag +) + +// BorderStyle Describe the separators above and below +type BorderStyle struct { + BorderTop, + BorderBottom, + BorderLeft, + BorderRight, + BorderTopLeft, + BorderTopRight, + BorderBottomLeft, + BorderBottomRight byte +} + +// DefaultBorderStyle Default table boundary separator +var DefaultBorderStyle = &BorderStyle{ + BorderTop: '-', + BorderBottom: '-', + BorderLeft: '|', + BorderRight: '|', + BorderTopLeft: '+', + BorderTopRight: '+', + BorderBottomLeft: '+', + BorderBottomRight: '+', +} + +// NewTable Returns the default Table style +func NewTable(w io.Writer) *Table { + table := new(Table) + table.BorderStyle = *DefaultBorderStyle + table.padding = 2 + table.w = w + table.width = 2 + table.maxCellWidth = make(map[int]int) + return table +} + +// SetBorder Set the separator for the specified location +func (table *Table) SetBorder(position uint, borderChar byte) *Table { + switch { + case position&TopFlag != 0: + table.BorderTop = borderChar + fallthrough + case position&BottomFlag != 0: + table.BorderBottom = borderChar + fallthrough + case position&LeftFlag != 0: + table.BorderLeft = borderChar + fallthrough + case position&RightFlag != 0: + table.BorderRight = borderChar + fallthrough + case position&TopLeftFlag != 0: + table.BorderTopLeft = borderChar + fallthrough + case position&TopRightFlag != 0: + table.BorderTopRight = borderChar + fallthrough + case position&BottomLeftFlag != 0: + table.BorderBottomLeft = borderChar + fallthrough + case position&BottomRightFlag != 0: + table.BorderBottomRight = borderChar + } + return table +} + +// ParentTable Return parent table +func (table *Table) ParentTable() *Table { + return table.parent +} + +// AddTitle Add title, centered display +func (table *Table) AddTitle(data string) *Table { + if strings.ContainsAny(data, "\t\n\r") { + panic("Illegal title") + } + str := title(data) + table.title = &str + width := separatorLen(data) + 6 + table.regularLen(width) + return table + +} + +// AddRow Increase the line, split the column with \t, \n split the line +func (table *Table) AddRow(data string) *Table { + var width int + data = strings.TrimSuffix(data, "\n") + rows := strings.Split(data, "\n") + for _, rowData := range rows { + width = 2 + if strings.Count(rowData, "\t")+1 > len(table.maxCellWidth) { + width += strings.Count(rowData, "\t") + } else { + width += len(table.maxCellWidth) - 1 + } + cols := strings.Split(rowData, "\t") + for i, v := range cols { + length := table.padding*2 + separatorLen(v) + if length > table.maxCellWidth[i] { + table.maxCellWidth[i] = length + } + width += table.maxCellWidth[i] + } + table.regularLen(width) + table.cells = append(table.cells, row(cols)) + } + return table +} + +// AddNewTable Add a new form to the form and return to the new form +func (table *Table) AddNewTable(w io.Writer) *Table { + subTable := new(Table) + subTable.BorderStyle = table.BorderStyle + subTable.padding = table.padding + subTable.w = w + subTable.width = table.width - 2 + subTable.maxCellWidth = make(map[int]int) + subTable.parent = table + table.subTable = append(table.subTable, subTable) + return subTable +} + +// AddTable Add an existing table and return to the parent table +func (table *Table) AddTable(subTable *Table) *Table { + table.subTable = append(table.subTable, subTable) + subTable.parent = table + if subTable.width < table.width { + subTable.regularLen(table.width - 2) + } else { + table.regularLen(subTable.width + 2) + } + + return table +} + +func (table *Table) Flush() { + var ( + count int + data string + tableTemp = table + ) + for tableTemp.parent != nil { + count++ + tableTemp = tableTemp.parent + } + parentSpaces := strings.Repeat("|", count) + separator := parentSpaces + table.flushSeparator() + parentSpaces + "\n" + io.WriteString(table.w, separator) + if table.title != nil { + data = parentSpaces + table.title.flush(table) + parentSpaces + "\n" + io.WriteString(table.w, data) + io.WriteString(table.w, separator) + } + for _, v := range table.cells { + data = parentSpaces + v.flush(table) + parentSpaces + "\n" + io.WriteString(table.w, data) + } + for _, v := range table.subTable { + v.Flush() + } + if table.subTable == nil { + io.WriteString(table.w, separator) + } + +} + +// IsEmptyCell Determine if the table contains any cells, not including return true +func (table *Table) IsEmptyCell() bool { + if len(table.cells) != 0 { + return false + } + return true +} + +// IsEmptySub Determine if the table contains any child tables, does not contain return true +func (table *Table) IsEmptySub() bool { + if len(table.subTable) != 0 { + return false + } + return true +} + +// Remove Delete subtable +func (table *Table) Remove(subT *Table) { + for i, v := range table.subTable { + if v == subT { + table.subTable = append(table.subTable[:i], table.subTable[i+1:]...) + } + } +} + +func (t title) flush(table *Table) string { + var data []rune + data = append(data, '|') + preOffset := (table.width - 2 - separatorLen(string(t))) / 2 + preSpaces := strings.Repeat(" ", preOffset) + data = append(data, []rune(preSpaces)...) + data = append(data, []rune(string(t))...) + sufOffset := table.width - preOffset - separatorLen(string(t)) - 2 + sufSpaces := strings.Repeat(" ", sufOffset) + data = append(data, []rune(sufSpaces)...) + data = append(data, '|') + return string(data) +} + +func (table *Table) flushSeparator() string { + var data []rune + data = append(data, rune(table.BorderTopLeft)) + data = append(data, []rune(strings.Repeat(string(table.BorderTop), table.width-2))...) + data = append(data, rune(table.BorderTopRight)) + return string(data) +} + +func (r row) flush(table *Table) string { + var data []rune + data = append(data, '|') + tempWidth := table.width - 1 - len(table.maxCellWidth) + for _, v := range table.maxCellWidth { + tempWidth -= v + } + displacement := tempWidth / len(table.maxCellWidth) + offset := tempWidth % len(table.maxCellWidth) + for i := range table.maxCellWidth { + table.maxCellWidth[i] += displacement + } + for i := 1; i <= offset; i++ { + table.maxCellWidth[len(table.maxCellWidth)-i]++ + } + preSpaces := strings.Repeat(" ", table.padding) + for i := 0; i < len(table.maxCellWidth); i++ { + var ( + column string + sufSpaces string + ) + if i > len(r)-1 { + column = "" + sufSpaces = strings.Repeat(" ", table.maxCellWidth[i]-table.padding) + } else { + column = r[i] + sufSpaces = strings.Repeat(" ", table.maxCellWidth[i]-separatorLen(column)-table.padding) + } + data = append(data, []rune(preSpaces)...) + data = append(data, []rune(column)...) + data = append(data, []rune(sufSpaces)...) + data = append(data, '|') + } + return string(data) +} +func (table *Table) regularLen(length int) { + if length > table.width { + table.width = length + if table.ParentTable() != nil { + table.ParentTable().regularLen(length + 2) + } + for _, v := range table.subTable { + v.regularLen(length - 2) + } + } +} +func separatorLen(w string) (length int) { + for _, v := range w { + if len(string(v)) == 1 { + length++ + } else { + length += 2 + } + } + return length +} diff --git a/humanFormat/table_test.go b/humanFormat/table_test.go new file mode 100644 index 000000000..a5bddfabf --- /dev/null +++ b/humanFormat/table_test.go @@ -0,0 +1,107 @@ +// Copyright 1999-2019 Alibaba Group Holding Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package format + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +var expectStr = `+------------------------------------------------------+ +| DescribeReigons | ++------------------------------------------------------+ +| RequestId | A71F2A35-BA66-496F-8B3F-BC19F4268937 | +|+----------------------------------------------------+| +|| Regions || +|+----------------------------------------------------+| +|| testcell | || +||+--------------------------------------------------+|| +||| Region ||| +||+--------------------------------------------------+|| +||| RegionId | cn-qingdao ||| +||| RegionEndpoint | ecs.aliyuncs.com ||| +||| LocalName | 华北 1 ||| +||+--------------------------------------------------+|| +||+--------------------------------------------------+|| +||+--------------------------------------------------+|| +` +var jsonData = `{ + "RequestId": "A71F2A35-BA66-496F-8B3F-BC19F4268937", + "Regions": { + "Region": [ + { + "RegionId": "cn-qingdao", + "RegionEndpoint": "ecs.aliyuncs.com", + "LocalName": "华北 1" + }, + { + "RegionId": "cn-beijing", + "RegionEndpoint": "ecs.aliyuncs.com", + "LocalName": "华北 2" + } + ] + } +}` +var jsonTable = `+------------------------------------------------------+ +| DescribeRegions | ++------------------------------------------------------+ +| RequestId | A71F2A35-BA66-496F-8B3F-BC19F4268937 | +|+----------------------------------------------------+| +|| Regions || +|+----------------------------------------------------+| +||+--------------------------------------------------+|| +||| Region ||| +||+--------------------------------------------------+|| +||| LocalName | 华北 1 ||| +||| RegionEndpoint | ecs.aliyuncs.com ||| +||| RegionId | cn-qingdao ||| +||+--------------------------------------------------+|| +||+--------------------------------------------------+|| +||| Region ||| +||+--------------------------------------------------+|| +||| LocalName | 华北 2 ||| +||| RegionEndpoint | ecs.aliyuncs.com ||| +||| RegionId | cn-beijing ||| +||+--------------------------------------------------+|| +` + +func TestTable(t *testing.T) { + + // test table + buf := new(bytes.Buffer) + table := NewTable(buf) + table2 := NewTable(buf) + table. + AddRow("RequestId\tA71F2A35-BA66-496F-8B3F-BC19F4268937").AddTitle("DescribeReigons"). + AddNewTable(buf).AddTitle("Regions"). + AddNewTable(buf).AddTitle("Region").AddRow("RegionId\tcn-qingdao").AddRow("RegionEndpoint\tecs.aliyuncs.com").AddRow("LocalName\t华北 1").ParentTable().AddRow("testcell\t"). + AddTable(table2) + table.Flush() + assert.Equal(t, expectStr, buf.String()) + assert.True(t, table2.IsEmptyCell()) + assert.True(t, table2.IsEmptySub()) + table.subTable[0].subTable[0].Remove(table2) + assert.Equal(t, 0, len(table.subTable[0].subTable[0].subTable)) + + // test json to table + buf.Reset() + table = NewTable(buf) + table.AddTitle("DescribeRegions") + FromJSON([]byte(jsonData), table) + table.Flush() + assert.Equal(t, jsonTable, buf.String()) +} diff --git a/openapi/commando.go b/openapi/commando.go index ce83292e3..9faedba4b 100644 --- a/openapi/commando.go +++ b/openapi/commando.go @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + package openapi import ( @@ -20,8 +21,6 @@ import ( "github.com/aliyun/aliyun-cli/i18n" "github.com/aliyun/aliyun-cli/meta" - "bytes" - "encoding/json" "fmt" "io" "strings" @@ -155,29 +154,30 @@ func (c *Commando) processInvoke(ctx *cli.Context, productCode string, apiOrMeth return nil } + if format, ok := ctx.Flags().GetValue("filter"); ok { + out, _ = FilterHandler(format, out) + } + if format, ok := ctx.Flags().GetValue("format"); ok { + f := FormatHandler(format) + if f == nil { + return fmt.Errorf("\nCLIERROR: unsupported format '%s'", format) + } + out, err = f.Format(apiOrMethod, out) + } else { + out, err = FormatHandler("json").Format(apiOrMethod, out) + } + // process `--output ...` if filter := GetOutputFilter(ctx); filter != nil { out, err = filter.FilterOutput(out) - if err != nil { - return err - } } - - out = FormatJson(out) - + if err != nil { + return err + } cli.Println(ctx.Writer(), out) return nil } -func FormatJson(content string) string { - var buf bytes.Buffer - err := json.Indent(&buf, []byte(content), "", "\t") - if err == nil { - content = buf.String() - } - return content -} - // invoke with helper func (c *Commando) invokeWithHelper(invoker Invoker) (resp string, err error, ok bool) { if pager := GetPager(); pager != nil { diff --git a/openapi/commando_test.go b/openapi/commando_test.go index 3305e8738..a0f08cc0c 100644 --- a/openapi/commando_test.go +++ b/openapi/commando_test.go @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + package openapi import ( @@ -203,11 +204,7 @@ func Test_processInvoke(t *testing.T) { OutputFlag(ctx.Flags()).SetAssigned(false) err = command.processInvoke(ctx, productCode, apiOrMethod, path) - assert.Nil(t, err) - - out := `{"requestid":"test","name":"json"}` - out = FormatJson(out) - assert.Equal(t, "{\n\t\"requestid\": \"test\",\n\t\"name\": \"json\"\n}", out) + assert.NotNil(t, err) } func Test_help(t *testing.T) { diff --git a/openapi/filter.go b/openapi/filter.go new file mode 100644 index 000000000..b616dda9d --- /dev/null +++ b/openapi/filter.go @@ -0,0 +1,40 @@ +// Copyright 1999-2019 Alibaba Group Holding Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package openapi + +import ( + "encoding/json" + "fmt" + + jmespath "github.com/jmespath/go-jmespath" +) + +// FilterHandler filter content with jmespath +func FilterHandler(jmesPath, data string) (string, error) { + var v interface{} + err := json.Unmarshal([]byte(data), &v) + if err != nil { + return "", fmt.Errorf("FilterHandler():json unmarshal ERROR > %s", err.Error()) + } + tempV, err := jmespath.Search(jmesPath, v) + if err != nil { + return "", fmt.Errorf("FilterHandler():jmesPath process error > %s", err.Error()) + } + dataByte, err := json.MarshalIndent(tempV, "", "\t") + if err != nil { + return "", fmt.Errorf("FilterHandler():json marshal error > %s", err.Error()) + } + return string(dataByte), nil +} diff --git a/openapi/filter_test.go b/openapi/filter_test.go new file mode 100644 index 000000000..6e9578230 --- /dev/null +++ b/openapi/filter_test.go @@ -0,0 +1,51 @@ +// Copyright 1999-2019 Alibaba Group Holding Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package openapi + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var jsonData = `{ + "RequestId": "A71F2A35-BA66-496F-8B3F-BC19F4268937", + "Regions": { + "Region": [ + { + "RegionId": "cn-qingdao", + "RegionEndpoint": "ecs.aliyuncs.com", + "LocalName": "华北 1" + }, + { + "RegionId": "cn-beijing", + "RegionEndpoint": "ecs.aliyuncs.com", + "LocalName": "华北 2" + } + ] + } +}` + +var jsonDataErr = `Error json Data Format` + +func TestFilter(t *testing.T) { + result, err := FilterHandler("RequestId", jsonData) + assert.Nil(t, err) + assert.Equal(t, `"A71F2A35-BA66-496F-8B3F-BC19F4268937"`, result) + + result, err = FilterHandler("*", jsonDataErr) + assert.NotNil(t, err) + assert.Empty(t, result) +} diff --git a/openapi/flags.go b/openapi/flags.go index ff8f8879b..7db65a733 100644 --- a/openapi/flags.go +++ b/openapi/flags.go @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + package openapi import ( @@ -33,6 +34,8 @@ func AddFlags(fs *cli.FlagSet) { fs.Add(NewDryRunFlag()) fs.Add(NewQuietFlag()) fs.Add(NewRoaFlag()) + fs.Add(NewFilterFlag()) + fs.Add(NewFormatFlag()) } const ( @@ -48,8 +51,16 @@ const ( DryRunFlagName = "dryrun" QuietFlagName = "quiet" OutputFlagName = "output" + FormatFlagName = "format" + FilterFlagName = "filter" ) +func FilterFlag(fs *cli.FlagSet) *cli.Flag { + return fs.Get(FilterFlagName) +} +func FormatFlag(fs *cli.FlagSet) *cli.Flag { + return fs.Get(FormatFlagName) +} func OutputFlag(fs *cli.FlagSet) *cli.Flag { return fs.Get(OutputFlagName) } @@ -109,6 +120,14 @@ func QuietFlag(fs *cli.FlagSet) *cli.Flag { // ), //} +func NewFilterFlag() *cli.Flag { + return &cli.Flag{ + Name: FilterFlagName, AssignedMode: cli.AssignedOnce, + Short: i18n.T( + "use `--filter` to filte result", + "使用 `--filter` 过滤结果")} +} + func NewSecureFlag() *cli.Flag { return &cli.Flag{Category: "caller", Name: SecureFlagName, AssignedMode: cli.AssignedNone, @@ -207,3 +226,14 @@ func NewQuietFlag() *cli.Flag { ExcludeWith: []string{DryRunFlagName}, } } + +func NewFormatFlag() *cli.Flag { + return &cli.Flag{Category: "config", + Name: FormatFlagName, + AssignedMode: cli.AssignedOnce, + Short: i18n.T( + "add `--quiet` to hide normal output", + "使用 `--quiet` 关闭正常输出", + ), + } +} diff --git a/openapi/format.go b/openapi/format.go new file mode 100644 index 000000000..80f9a0866 --- /dev/null +++ b/openapi/format.go @@ -0,0 +1,62 @@ +// Copyright 1999-2019 Alibaba Group Holding Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package openapi + +import ( + "bytes" + "encoding/json" + + format "github.com/aliyun/aliyun-cli/humanFormat" +) + +// Implement the format of outputformat interface + +type TableFormat string + +type JSONFormat string + +// TODO +// type TextFormat string + +type OutputFormat interface { + Format(apiOrMethod, data string) (string, error) +} + +// FormatHandler precess output format +func FormatHandler(format string) OutputFormat { + switch format { + case "table": + return TableFormat("table") + case "json": + return JSONFormat("json") + } + return nil +} + +// Format return table format +func (t TableFormat) Format(apiOrMethod, data string) (string, error) { + buf := new(bytes.Buffer) + table := format.NewTable(buf).AddTitle(apiOrMethod) + format.FromJSON([]byte(data), table) + table.Flush() + return buf.String(), nil +} + +// Format return json format +func (j JSONFormat) Format(apiOrMethod, data string) (string, error) { + buf := new(bytes.Buffer) + err := json.Indent(buf, []byte(data), "", "\t") + return buf.String(), err +} diff --git a/openapi/output_filter.go b/openapi/output_filter.go index bf6dc5c1f..a0ecc3bcb 100644 --- a/openapi/output_filter.go +++ b/openapi/output_filter.go @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + package openapi import ( @@ -44,6 +45,7 @@ func NewOutputFlag() *cli.Flag { {Key: "rows", Repeatable: false, Required: false}, {Key: "num", Repeatable: false, Required: false}, }, + ExcludeWith: []string{"filter", "format"}, } }