#109 client支持指定revision (#108)
* Improve client revision features
Signed-off-by: zhulijian <zhulijian1@huawei.com>
* ut
diff --git a/client/client.go b/client/client.go
index b70d465..422a537 100644
--- a/client/client.go
+++ b/client/client.go
@@ -25,6 +25,7 @@
"fmt"
"net/http"
"net/url"
+ "strconv"
"strings"
"github.com/apache/servicecomb-kie/pkg/common"
@@ -57,7 +58,7 @@
opts Config
cipher security.Cipher
c *httpclient.Requests
- CurrentRevision string
+ currentRevision int
}
//Config is the config of client
@@ -127,7 +128,7 @@
}
//Get get value of a key
-func (c *Client) Get(ctx context.Context, opts ...GetOption) (*model.KVResponse, error) {
+func (c *Client) Get(ctx context.Context, opts ...GetOption) (*model.KVResponse, int, error) {
options := GetOptions{}
for _, o := range opts {
o(&options)
@@ -135,12 +136,14 @@
if options.Project == "" {
options.Project = defaultProject
}
-
+ if options.Revision == "" {
+ options.Revision = strconv.Itoa(c.currentRevision)
+ }
var url string
if options.Key != "" {
- url = fmt.Sprintf("%s/%s/%s/%s/%s?revision=%s", c.opts.Endpoint, version, options.Project, APIPathKV, options.Key, c.CurrentRevision)
+ url = fmt.Sprintf("%s/%s/%s/%s/%s?revision=%s", c.opts.Endpoint, version, options.Project, APIPathKV, options.Key, options.Revision)
} else {
- url = fmt.Sprintf("%s/%s/%s/%s?revision=%s", c.opts.Endpoint, version, options.Project, APIPathKV, c.CurrentRevision)
+ url = fmt.Sprintf("%s/%s/%s/%s?revision=%s", c.opts.Endpoint, version, options.Project, APIPathKV, options.Revision)
}
if options.Wait != "" {
url = url + "&wait=" + options.Wait
@@ -158,31 +161,39 @@
h := http.Header{}
resp, err := c.c.Do(ctx, http.MethodGet, url, h, nil)
if err != nil {
- return nil, err
+ return nil, -1, err
+ }
+ responseRevision, err := strconv.Atoi(resp.Header.Get(common.HeaderRevision))
+ if err != nil {
+ responseRevision = -1
}
b := httputil.ReadBody(resp)
if resp.StatusCode != http.StatusOK {
if resp.StatusCode == http.StatusNotFound {
- return nil, ErrKeyNotExist
+ return nil, responseRevision, ErrKeyNotExist
}
if resp.StatusCode == http.StatusNotModified {
- return nil, ErrNoChanges
+ return nil, responseRevision, ErrNoChanges
}
openlogging.Error(MsgGetFailed, openlogging.WithTags(openlogging.Tags{
"k": options.Key,
"status": resp.Status,
"body": b,
}))
- return nil, fmt.Errorf(FmtGetFailed, options.Key, resp.Status, b)
+ return nil, responseRevision, fmt.Errorf(FmtGetFailed, options.Key, resp.Status, b)
+ } else if err != nil {
+ msg := fmt.Sprintf("get revision from response header failed when the request status is OK: %v", err)
+ openlogging.Error(msg)
+ return nil, responseRevision, fmt.Errorf(msg)
}
var kvs *model.KVResponse
err = json.Unmarshal(b, &kvs)
if err != nil {
openlogging.Error("unmarshal kv failed:" + err.Error())
- return nil, err
+ return nil, responseRevision, err
}
- c.CurrentRevision = resp.Header.Get(common.HeaderRevision)
- return kvs, nil
+ c.currentRevision = responseRevision
+ return kvs, responseRevision, nil
}
//Summary get value by labels
@@ -261,3 +272,8 @@
}
return nil
}
+
+//CurrentRevision return the current revision of kie, which is updated on the last get request
+func (c *Client) CurrentRevision() int {
+ return c.currentRevision
+}
diff --git a/client/client_test.go b/client/client_test.go
index b66a598..7c8fcd0 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -40,24 +40,32 @@
_, err := c.Put(context.TODO(), kv, WithProject("client_test"))
assert.NoError(t, err)
- kvs, _ := c.Get(context.TODO(),
+ kvs, responseRevision, _ := c.Get(context.TODO(),
WithKey("app.properties"),
WithGetProject("client_test"),
WithLabels(map[string]string{"service": "client"}))
assert.GreaterOrEqual(t, len(kvs.Data), 1)
- _, err = c.Get(context.TODO(),
+ _, _, err = c.Get(context.TODO(),
WithGetProject("client_test"),
- WithLabels(map[string]string{"service": "client"}))
+ WithLabels(map[string]string{"service": "client"}),
+ WithRevision(responseRevision))
assert.Equal(t, ErrNoChanges, err)
- _, err = c.Get(context.TODO(),
+ _, _, err = c.Get(context.TODO(),
+ WithGetProject("client_test"),
WithLabels(map[string]string{"service": "client"}))
assert.Error(t, err)
+ _, _, err = c.Get(context.TODO(),
+ WithGetProject("client_test"),
+ WithLabels(map[string]string{"service": "client"}),
+ WithRevision(c.CurrentRevision()-1))
+ assert.NoError(t, err)
+
t.Run("long polling,wait 10s,change value,should return result", func(t *testing.T) {
go func() {
- kvs, err = c.Get(context.TODO(),
+ kvs, _, err = c.Get(context.TODO(),
WithLabels(map[string]string{"service": "client"}),
WithGetProject("client_test"),
WithWait("10s"))
@@ -80,8 +88,8 @@
}
_, err := c.Put(context.TODO(), kv, WithProject("client_test"))
assert.NoError(t, err)
- t.Log(c.CurrentRevision)
- kvs, err = c.Get(context.TODO(),
+ t.Log(c.CurrentRevision())
+ kvs, _, err = c.Get(context.TODO(),
WithGetProject("client_test"),
WithLabels(map[string]string{"service": "client"}),
WithExact())
@@ -103,7 +111,7 @@
kvBody.Labels["env"] = "client_test"
kv, err := c.Put(context.TODO(), kvBody, WithProject("client_test"))
assert.NoError(t, err)
- kvs, err := c.Get(context.TODO(),
+ kvs, _, err := c.Get(context.TODO(),
WithKey("time"),
WithGetProject("client_test"),
WithLabels(map[string]string{"env": "client_test"}))
diff --git a/client/options.go b/client/options.go
index 6b2b9a7..f450e33 100644
--- a/client/options.go
+++ b/client/options.go
@@ -17,6 +17,8 @@
package client
+import "strconv"
+
const (
defaultProject = "default"
)
@@ -29,11 +31,12 @@
//GetOptions is the options of client func
type GetOptions struct {
- Labels []map[string]string
- Project string
- Key string
- Wait string
- Exact bool
+ Labels []map[string]string
+ Project string
+ Key string
+ Wait string
+ Exact bool
+ Revision string
}
//OpOptions is the options of client func
@@ -78,6 +81,13 @@
}
}
+//WithRevision query keys with certain revision
+func WithRevision(revision int) GetOption {
+ return func(options *GetOptions) {
+ options.Revision = strconv.Itoa(revision)
+ }
+}
+
//WithProject set project to param
func WithProject(project string) OpOption {
return func(options *OpOptions) {