This is an automated email from the ASF dual-hosted git repository. asifdxtreme pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/servicecomb-kie.git
commit 999a22414e4ff0340d883e28e149d83fcab60648 Author: tian <xiaoliang.t...@gmail.com> AuthorDate: Wed Jun 19 18:35:34 2019 +0800 support combination query --- go.mod | 3 +- pkg/common/common.go | 3 +- server/dao/kie_api.go | 7 +--- server/dao/kv_test.go | 8 ++-- server/resource/v1/common.go | 31 +++++++++++---- server/resource/v1/common_test.go | 31 +++++++++++++++ server/resource/v1/doc_struct.go | 14 ++++--- server/resource/v1/kv_resource.go | 77 ++++++++++++++----------------------- server/resource/v1/v1_suite_test.go | 13 +++++++ 9 files changed, 113 insertions(+), 74 deletions(-) diff --git a/go.mod b/go.mod index d5efedc..6646204 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,12 @@ require ( github.com/emicklei/go-restful v2.8.0+incompatible github.com/go-chassis/foundation v0.0.0-20190516083152-b8b2476b6db7 github.com/go-chassis/go-archaius v0.16.0 - github.com/go-chassis/go-chassis v1.4.1 + github.com/go-chassis/go-chassis v1.4.3 github.com/go-chassis/paas-lager v1.0.2-0.20190328010332-cf506050ddb2 github.com/go-mesh/openlogging v1.0.1-0.20181205082104-3d418c478b2d github.com/onsi/ginkgo v1.8.0 github.com/onsi/gomega v1.5.0 + github.com/pkg/errors v0.8.0 github.com/stretchr/testify v1.2.2 github.com/urfave/cli v1.20.0 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect diff --git a/pkg/common/common.go b/pkg/common/common.go index e39dca6..f111bce 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -19,8 +19,7 @@ package common //match mode const ( - MatchGreedy = "greedy" - MatchExact = "exact" + QueryParamQ = "q" ) //http headers diff --git a/server/dao/kie_api.go b/server/dao/kie_api.go index a5cca3d..0e0020d 100644 --- a/server/dao/kie_api.go +++ b/server/dao/kie_api.go @@ -191,7 +191,7 @@ func (s *MongodbService) FindKVByLabelID(ctx context.Context, domain, labelID, k //FindKV get kvs by key, labels //because labels has a a lot of combination, -//you can use WithExactLabels to return only one kv which's labels exactly match the criteria +//you can use WithDepth(0) to return only one kv which's labels exactly match the criteria func (s *MongodbService) FindKV(ctx context.Context, domain string, options ...FindOption) ([]*model.KVResponse, error) { opts := FindOptions{} for _, o := range options { @@ -208,7 +208,7 @@ func (s *MongodbService) FindKV(ctx context.Context, domain string, options ...F defer cur.Close(ctx) kvResp := make([]*model.KVResponse, 0) - if opts.ExactLabels { + if opts.Depth == 0 { openlogging.Debug("find one key", openlogging.WithTags( map[string]interface{}{ "key": opts.Key, @@ -218,9 +218,6 @@ func (s *MongodbService) FindKV(ctx context.Context, domain string, options ...F )) return cursorToOneKV(ctx, cur, opts.Labels) } - if opts.Depth == 0 { - opts.Depth = 1 - } for cur.Next(ctx) { curKV := &model.KVDoc{} diff --git a/server/dao/kv_test.go b/server/dao/kv_test.go index efbe6d4..db92857 100644 --- a/server/dao/kv_test.go +++ b/server/dao/kv_test.go @@ -132,8 +132,8 @@ var _ = Describe("Kv mongodb service", func() { It("should not return err", func() { Expect(err).Should(BeNil()) }) - It("should has 2 records", func() { - Expect(len(kvs)).Should(Equal(2)) + It("should has 1 records", func() { + Expect(len(kvs)).Should(Equal(1)) }) }) @@ -189,8 +189,8 @@ var _ = Describe("Kv mongodb service", func() { It("should not return err", func() { Expect(err).Should(BeNil()) }) - It("should has 2 records", func() { - Expect(len(kvs)).Should(Equal(2)) + It("should has 1 records", func() { + Expect(len(kvs)).Should(Equal(1)) }) }) diff --git a/server/resource/v1/common.go b/server/resource/v1/common.go index 48baa22..dcf9901 100644 --- a/server/resource/v1/common.go +++ b/server/resource/v1/common.go @@ -22,15 +22,17 @@ import ( "fmt" "github.com/apache/servicecomb-kie/pkg/common" "github.com/apache/servicecomb-kie/pkg/model" + goRestful "github.com/emicklei/go-restful" "github.com/go-chassis/go-chassis/server/restful" "github.com/go-mesh/openlogging" + "github.com/pkg/errors" "strconv" + "strings" ) //const of server const ( MsgDomainMustNotBeEmpty = "domain must not be empty" - MsgIllegalFindPolicy = "value of header " + common.HeaderMatch + " can be greedy or exact" MsgIllegalLabels = "label's value can not be empty, " + "label can not be duplicated, please check your query parameters" MsgIllegalDepth = "X-Depth must be number" @@ -55,14 +57,27 @@ func ReadFindDepth(context *restful.Context) (int, error) { return depth, nil } -//ReadMatchPolicy get match policy -func ReadMatchPolicy(context *restful.Context) string { - policy := context.ReadRestfulRequest().HeaderParameter(common.HeaderMatch) - if policy == "" { - //default is exact to reduce network traffic - return common.MatchExact +//ReadLabelCombinations get query combination from url +//q=app:default+service:payment&q=app:default +func ReadLabelCombinations(req *goRestful.Request) ([]map[string]string, error) { + queryCombinations := req.QueryParameters(common.QueryParamQ) + labelCombinations := make([]map[string]string, 0) + for _, queryStr := range queryCombinations { + labelStr := strings.Split(queryStr, " ") + labels := make(map[string]string, len(labelStr)) + for _, label := range labelStr { + l := strings.Split(label, ":") + if len(l) != 2 { + return nil, errors.New("wrong query syntax:" + label) + } + labels[l[0]] = l[1] + } + if len(labels) == 0 { + continue + } + labelCombinations = append(labelCombinations, labels) } - return policy + return labelCombinations, nil } //WriteErrResponse write error message to client diff --git a/server/resource/v1/common_test.go b/server/resource/v1/common_test.go new file mode 100644 index 0000000..6f3b139 --- /dev/null +++ b/server/resource/v1/common_test.go @@ -0,0 +1,31 @@ +package v1_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + . "github.com/apache/servicecomb-kie/server/resource/v1" + "github.com/emicklei/go-restful" + "net/http" +) + +var _ = Describe("Common", func() { + Describe("set query combination", func() { + Context("valid param", func() { + r, err := http.NewRequest("GET", + "/kv?q=app:mall+service:payment&q=app:mall+service:payment+version:1.0.0", + nil) + It("should not return err ", func() { + Expect(err).Should(BeNil()) + }) + c, err := ReadLabelCombinations(restful.NewRequest(r)) + It("should not return err ", func() { + Expect(err).Should(BeNil()) + }) + It("should has 2 combinations", func() { + Expect(len(c)).Should(Equal(2)) + }) + + }) + }) +}) diff --git a/server/resource/v1/doc_struct.go b/server/resource/v1/doc_struct.go index 37f7432..33f0eb7 100644 --- a/server/resource/v1/doc_struct.go +++ b/server/resource/v1/doc_struct.go @@ -31,17 +31,19 @@ var ( ParamType: goRestful.HeaderParameterKind, Desc: "integer, default is 1, if you set match policy, you can set,depth to decide label number", } + DocQueryCombination = &restful.Parameters{ + DataType: "string", + Name: common.QueryParamQ, + ParamType: goRestful.QueryParameterKind, + Desc: "the combination format is {label_key}:{label_value}+{label_key}:{label_value} " + + "for example: /v1/kv?q=app:mall&q=app:mall+service:cart " + + "that will query key values from 2 kinds of labels", + } DocPathKey = &restful.Parameters{ DataType: "string", Name: "key", ParamType: goRestful.PathParameterKind, } - DocHeaderMath = &restful.Parameters{ - DataType: "string", - Name: common.HeaderMatch, - ParamType: goRestful.HeaderParameterKind, - Desc: "greedy or exact", - } ) //KVBody is open api doc diff --git a/server/resource/v1/kv_resource.go b/server/resource/v1/kv_resource.go index a1f94b6..c072dc6 100644 --- a/server/resource/v1/kv_resource.go +++ b/server/resource/v1/kv_resource.go @@ -21,7 +21,6 @@ package v1 import ( "encoding/json" "fmt" - "github.com/apache/servicecomb-kie/pkg/common" "github.com/apache/servicecomb-kie/pkg/model" "github.com/apache/servicecomb-kie/server/dao" goRestful "github.com/emicklei/go-restful" @@ -67,8 +66,8 @@ func (r *KVResource) Put(context *restful.Context) { } -//FindWithKey search key by label and key -func (r *KVResource) FindWithKey(context *restful.Context) { +//GetByKey search key by label and key +func (r *KVResource) GetByKey(context *restful.Context) { var err error key := context.ReadPathParameter("key") if key == "" { @@ -94,23 +93,12 @@ func (r *KVResource) FindWithKey(context *restful.Context) { WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty) return } - policy := ReadMatchPolicy(context) d, err := ReadFindDepth(context) if err != nil { WriteErrResponse(context, http.StatusBadRequest, MsgIllegalDepth) return } - var kvs []*model.KVResponse - switch policy { - case common.MatchGreedy: - kvs, err = s.FindKV(context.Ctx, domain.(string), dao.WithKey(key), dao.WithLabels(labels), dao.WithDepth(d)) - case common.MatchExact: - kvs, err = s.FindKV(context.Ctx, domain.(string), dao.WithKey(key), dao.WithLabels(labels), - dao.WithExactLabels()) - default: - WriteErrResponse(context, http.StatusBadRequest, MsgIllegalFindPolicy) - return - } + kvs, err := s.FindKV(context.Ctx, domain.(string), dao.WithKey(key), dao.WithLabels(labels), dao.WithDepth(d)) if err == dao.ErrKeyNotExists { WriteErrResponse(context, http.StatusNotFound, err.Error()) return @@ -126,17 +114,13 @@ func (r *KVResource) FindWithKey(context *restful.Context) { } -//FindByLabels search key only by label -func (r *KVResource) FindByLabels(context *restful.Context) { +//SearchByLabels search key only by label +func (r *KVResource) SearchByLabels(context *restful.Context) { var err error - values := context.ReadRequest().URL.Query() - labels := make(map[string]string, len(values)) - for k, v := range values { - if len(v) != 1 { - WriteErrResponse(context, http.StatusBadRequest, MsgIllegalLabels) - return - } - labels[k] = v[0] + labelCombinations, err := ReadLabelCombinations(context.ReadRestfulRequest()) + if err != nil { + WriteErrResponse(context, http.StatusBadRequest, err.Error()) + return } s, err := dao.NewKVService() if err != nil { @@ -148,27 +132,24 @@ func (r *KVResource) FindByLabels(context *restful.Context) { WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty) return } - policy := ReadMatchPolicy(context) - d, err := ReadFindDepth(context) - if err != nil { - WriteErrResponse(context, http.StatusBadRequest, MsgIllegalDepth) - return - } var kvs []*model.KVResponse - switch policy { - case common.MatchGreedy: - kvs, err = s.FindKV(context.Ctx, domain.(string), dao.WithLabels(labels), dao.WithDepth(d)) - case common.MatchExact: - kvs, err = s.FindKV(context.Ctx, domain.(string), dao.WithLabels(labels), - dao.WithExactLabels()) - default: - WriteErrResponse(context, http.StatusBadRequest, MsgIllegalFindPolicy) - return + for _, labels := range labelCombinations { + result, err := s.FindKV(context.Ctx, domain.(string), dao.WithLabels(labels)) + if err != nil { + if err == dao.ErrKeyNotExists { + continue + } + WriteErrResponse(context, http.StatusInternalServerError, err.Error()) + return + } + kvs = append(kvs, result...) + } - if err == dao.ErrKeyNotExists { + if len(kvs) == 0 { WriteErrResponse(context, http.StatusNotFound, err.Error()) return } + err = context.WriteHeaderAndJSON(http.StatusOK, kvs, goRestful.MIME_JSON) if err != nil { openlogging.Error(err.Error()) @@ -215,7 +196,7 @@ func (r *KVResource) URLPatterns() []restful.Route { DataType: "string", Name: "X-Realm", ParamType: goRestful.HeaderParameterKind, - Desc: "set kv to heterogeneous config server", + Desc: "set kv to heterogeneous config server, not implement yet", }, }, Returns: []*restful.Returns{ @@ -230,7 +211,7 @@ func (r *KVResource) URLPatterns() []restful.Route { }, { Method: http.MethodGet, Path: "/v1/kv/{key}", - ResourceFuncName: "FindWithKey", + ResourceFuncName: "GetByKey", FuncDesc: "get key values by key and labels", Parameters: []*restful.Parameters{ DocPathKey, DocHeaderMath, DocHeaderDepth, @@ -239,7 +220,7 @@ func (r *KVResource) URLPatterns() []restful.Route { { Code: http.StatusOK, Message: "get key value success", - Model: []*KVBody{}, + Model: []*model.KVResponse{}, }, }, Consumes: []string{goRestful.MIME_JSON}, @@ -248,16 +229,16 @@ func (r *KVResource) URLPatterns() []restful.Route { }, { Method: http.MethodGet, Path: "/v1/kv", - ResourceFuncName: "FindByLabels", - FuncDesc: "find key values only by labels", + ResourceFuncName: "SearchByLabels", + FuncDesc: "search key values by labels combination", Parameters: []*restful.Parameters{ - DocHeaderMath, DocHeaderDepth, + DocHeaderMath, DocQueryCombination, }, Returns: []*restful.Returns{ { Code: http.StatusOK, Message: "get key value success", - Model: []*KVBody{}, + Model: []*model.KVResponse{}, }, }, Consumes: []string{goRestful.MIME_JSON}, diff --git a/server/resource/v1/v1_suite_test.go b/server/resource/v1/v1_suite_test.go new file mode 100644 index 0000000..23b7482 --- /dev/null +++ b/server/resource/v1/v1_suite_test.go @@ -0,0 +1,13 @@ +package v1_test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestV1(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "V1 Suite") +}