mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-22 10:08:15 -05:00
fix commands issues (#492)
* fix api commands output * remove unused code * fix convert always has -r * update merge err to locate failed file
This commit is contained in:
parent
8702812735
commit
a5e24544a2
@ -18,6 +18,7 @@ package merge
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"v2ray.com/core/common/cmdarg"
|
||||
@ -73,14 +74,14 @@ func loadFiles(args []string) (map[string]interface{}, error) {
|
||||
for _, arg := range args {
|
||||
r, err := cmdarg.LoadArg(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("fail to load %s: %s", arg, err)
|
||||
}
|
||||
m, err := decode(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("fail to decode %s: %s", arg, err)
|
||||
}
|
||||
if err = mergeMaps(c, m); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("fail to merge %s: %s", arg, err)
|
||||
}
|
||||
}
|
||||
return c, nil
|
||||
|
@ -2,7 +2,10 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -11,8 +14,6 @@ import (
|
||||
"v2ray.com/core/main/commands/base"
|
||||
)
|
||||
|
||||
type serviceHandler func(ctx context.Context, conn *grpc.ClientConn, cmd *base.Command, args []string) string
|
||||
|
||||
var (
|
||||
apiServerAddrPtr string
|
||||
apiTimeout int
|
||||
@ -39,16 +40,55 @@ func dialAPIServer() (conn *grpc.ClientConn, ctx context.Context, close func())
|
||||
}
|
||||
|
||||
func showResponese(m proto.Message) {
|
||||
msg := ""
|
||||
bs, err := proto.Marshal(m)
|
||||
if err != nil {
|
||||
msg = err.Error()
|
||||
} else {
|
||||
msg = string(bs)
|
||||
msg = strings.TrimSpace(msg)
|
||||
}
|
||||
if msg == "" {
|
||||
if isEmpty(m) {
|
||||
// avoid outputs like `{}`, `{"key":{}}`
|
||||
return
|
||||
}
|
||||
fmt.Println(msg)
|
||||
b := new(strings.Builder)
|
||||
e := json.NewEncoder(b)
|
||||
e.SetIndent("", " ")
|
||||
e.SetEscapeHTML(false)
|
||||
err := e.Encode(m)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stdout, "%v\n", m)
|
||||
base.Fatalf("error encode json: %s", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(strings.TrimSpace(b.String()))
|
||||
}
|
||||
|
||||
// isEmpty checks if the response is empty (all zero values).
|
||||
// proto.Message types always "omitempty" on fields,
|
||||
// there's no chance for a response to show zero-value messages,
|
||||
// so we can perform isZero test here
|
||||
func isEmpty(response interface{}) bool {
|
||||
s := reflect.Indirect(reflect.ValueOf(response))
|
||||
if s.Kind() == reflect.Invalid {
|
||||
return true
|
||||
}
|
||||
switch s.Kind() {
|
||||
case reflect.Struct:
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Type().Field(i)
|
||||
if f.Name[0] < 65 || f.Name[0] > 90 {
|
||||
// continue if not exported.
|
||||
continue
|
||||
}
|
||||
field := s.Field(i)
|
||||
if !isEmpty(field.Interface()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
if !isEmpty(s.Index(i).Interface()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
default:
|
||||
if !s.IsZero() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
140
main/commands/all/api/shared_test.go
Normal file
140
main/commands/all/api/shared_test.go
Normal file
@ -0,0 +1,140 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
statsService "v2ray.com/core/app/stats/command"
|
||||
)
|
||||
|
||||
func TestEmptyResponese_0(t *testing.T) {
|
||||
r := &statsService.QueryStatsResponse{
|
||||
Stat: []*statsService.Stat{
|
||||
{
|
||||
Name: "1>>2",
|
||||
Value: 1,
|
||||
},
|
||||
{
|
||||
Name: "1>>2>>3",
|
||||
Value: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
assert(t, isEmpty(r), false)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_1(t *testing.T) {
|
||||
r := (*statsService.QueryStatsResponse)(nil)
|
||||
assert(t, isEmpty(r), true)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_2(t *testing.T) {
|
||||
r := &statsService.QueryStatsResponse{
|
||||
Stat: nil,
|
||||
}
|
||||
assert(t, isEmpty(r), true)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_3(t *testing.T) {
|
||||
r := &statsService.QueryStatsResponse{
|
||||
Stat: []*statsService.Stat{},
|
||||
}
|
||||
assert(t, isEmpty(r), true)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_4(t *testing.T) {
|
||||
r := &statsService.QueryStatsResponse{
|
||||
Stat: []*statsService.Stat{
|
||||
{
|
||||
Name: "",
|
||||
Value: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
assert(t, isEmpty(r), true)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_5(t *testing.T) {
|
||||
type test struct {
|
||||
Value *statsService.QueryStatsResponse
|
||||
}
|
||||
r := &test{
|
||||
Value: &statsService.QueryStatsResponse{
|
||||
Stat: []*statsService.Stat{
|
||||
{
|
||||
Name: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert(t, isEmpty(r), true)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_6(t *testing.T) {
|
||||
type test struct {
|
||||
Value *statsService.QueryStatsResponse
|
||||
}
|
||||
r := &test{
|
||||
Value: &statsService.QueryStatsResponse{
|
||||
Stat: []*statsService.Stat{
|
||||
{
|
||||
Value: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert(t, isEmpty(r), false)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_7(t *testing.T) {
|
||||
type test struct {
|
||||
Value *int
|
||||
}
|
||||
v := 1
|
||||
r := &test{
|
||||
Value: &v,
|
||||
}
|
||||
assert(t, isEmpty(r), false)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_8(t *testing.T) {
|
||||
type test struct {
|
||||
Value *int
|
||||
}
|
||||
v := 0
|
||||
r := &test{
|
||||
Value: &v,
|
||||
}
|
||||
assert(t, isEmpty(r), true)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_9(t *testing.T) {
|
||||
assert(t, isEmpty(0), true)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_10(t *testing.T) {
|
||||
assert(t, isEmpty(1), false)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_11(t *testing.T) {
|
||||
r := []*statsService.Stat{
|
||||
{
|
||||
Name: "",
|
||||
},
|
||||
}
|
||||
assert(t, isEmpty(r), true)
|
||||
}
|
||||
|
||||
func TestEmptyResponese_12(t *testing.T) {
|
||||
r := []*statsService.Stat{
|
||||
{
|
||||
Value: 1,
|
||||
},
|
||||
}
|
||||
assert(t, isEmpty(r), false)
|
||||
}
|
||||
|
||||
func assert(t *testing.T, value, expected bool) {
|
||||
if value != expected {
|
||||
t.Fatalf("Expected: %v, actual: %v", expected, value)
|
||||
}
|
||||
}
|
@ -72,7 +72,7 @@ func setConfArgs(cmd *base.Command) {
|
||||
cmd.Flag.StringVar(&inputFormat, "i", "json", "")
|
||||
cmd.Flag.StringVar(&outputFormat, "output", "json", "")
|
||||
cmd.Flag.StringVar(&outputFormat, "o", "json", "")
|
||||
cmd.Flag.BoolVar(&confDirRecursively, "r", true, "")
|
||||
cmd.Flag.BoolVar(&confDirRecursively, "r", false, "")
|
||||
}
|
||||
func executeConvert(cmd *base.Command, args []string) {
|
||||
setConfArgs(cmd)
|
||||
|
Loading…
Reference in New Issue
Block a user