This is an automated email from the ASF dual-hosted git repository.
tianxiaoliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-kie.git
The following commit(s) were added to refs/heads/master by this push:
new 80dc785 refactor apis (#129)
80dc785 is described below
commit 80dc785b4b1f303174480de772542d34cd1b700f
Author: zhulijian <[email protected]>
AuthorDate: Sat Mar 28 14:10:21 2020 +0800
refactor apis (#129)
* refactor apis of create and update kv
Signed-off-by: zhulijian <[email protected]>
* refactor apis of get and delete kv
Signed-off-by: zhulijian <[email protected]>
* ut
---
docs/api.yaml | 144 ++++++++++-----------
pkg/common/common.go | 32 ++---
server/handler/track_handler.go | 2 +-
server/resource/v1/common.go | 56 +++++++-
server/resource/v1/doc_struct.go | 29 +++--
server/resource/v1/history_resource.go | 16 +--
server/resource/v1/kv_resource.go | 222 +++++++++++++++++++++-----------
server/resource/v1/kv_resource_test.go | 111 ++++++++++++++--
server/service/mongo/kv/kv_service.go | 98 ++++++++++----
server/service/mongo/kv/kv_test.go | 58 ++++++---
server/service/mongo/kv/tool.go | 19 +--
server/service/mongo/session/session.go | 2 +
server/service/service.go | 4 +-
13 files changed, 525 insertions(+), 268 deletions(-)
diff --git a/docs/api.yaml b/docs/api.yaml
index 4171d65..c45264b 100644
--- a/docs/api.yaml
+++ b/docs/api.yaml
@@ -13,6 +13,9 @@ paths:
in: path
required: true
type: string
+ - name: key
+ in: query
+ type: string
- name: label
in: query
description: label pairs,for example
&label=service:order&label=version:1.0.0
@@ -61,78 +64,49 @@ paths:
type: integer
"304":
description: empty body
- "404":
- description: ""
- headers:
- X-Kie-Revision:
- description: cluster latest revision number, if key value is
changed,
- it will increase.
- type: integer
- delete:
- summary: delete key by kv ID.
- operationId: Delete
+ post:
+ summary: create a key value
+ operationId: Post
parameters:
- name: project
in: path
required: true
type: string
- - name: kv_id
- in: query
+ - name: Content-Type
+ in: header
+ description: used to indicate the media type of the resource, the
value can
+ be application/json or text/yaml
required: true
type: string
+ - name: body
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/v1.KVCreateBody'
consumes:
- - '*/*'
+ - application/json
+ - text/yaml
produces:
- - '*/*'
+ - application/json
+ - text/yaml
responses:
- "204":
- description: Delete success
- "500":
- description: Server error
- /v1/{project}/kie/kv/{key}:
+ "200":
+ description: ""
+ schema:
+ $ref: '#/definitions/DocResponseSingleKey'
+ /v1/{project}/kie/kv/{kv_id}:
get:
- summary: get key values by key and labels
- operationId: GetByKey
+ summary: get key values by kv_id
+ operationId: Get
parameters:
- name: project
in: path
required: true
type: string
- - name: key
+ - name: kv_id
in: path
required: true
type: string
- - name: label
- in: query
- description: label pairs,for example
&label=service:order&label=version:1.0.0
- type: string
- - name: wait
- in: query
- description: wait until any kv changed. for example wait=5s, server
will not
- response until 5 seconds, during that time window, if any kv
changed, server
- will return 200 and kv list, otherwise return 304 and empty body
- type: string
- - name: match
- in: query
- description: match works with label query param, it specifies label
match
- pattern. if it is empty, server will return kv which's labels
partial match
- the label query param. uf it is exact, server will return kv
which's labels
- exact match the label query param
- type: string
- - name: revision
- in: query
- description: each time you query,server will return a number in
header X-Kie-Revision.
- you can record it in client side, use this number as param value.
if current
- revision is greater than it, server will return data
- type: string
- - name: limit
- in: query
- description: pagination
- type: string
- - name: offset
- in: query
- description: pagination
- type: string
consumes:
- '*/*'
produces:
@@ -142,43 +116,37 @@ paths:
"200":
description: get key value success
schema:
- $ref: '#/definitions/DocResponseGetKey'
+ $ref: '#/definitions/DocResponseSingleKey'
headers:
X-Kie-Revision:
description: cluster latest revision number, if key value is
changed,
it will increase.
type: integer
- "304":
- description: empty body
"404":
- description: ""
- headers:
- X-Kie-Revision:
- description: cluster latest revision number, if key value is
changed,
- it will increase.
- type: integer
+ description: key value not found
put:
- summary: create or update key value
+ summary: update a key value
operationId: Put
parameters:
- name: project
in: path
required: true
type: string
- - name: key
+ - name: kv_id
in: path
required: true
type: string
- name: Content-Type
in: header
+ description: used to indicate the media type of the resource, the
value can
+ be application/json or text/yaml
required: true
- description: used to indicate the media type of the resource, the
value can be application/json or text/yaml
type: string
- name: body
in: body
required: true
schema:
- $ref: '#/definitions/v1.KVBody'
+ $ref: '#/definitions/v1.KVUpdateBody'
consumes:
- application/json
- text/yaml
@@ -190,7 +158,30 @@ paths:
description: ""
schema:
$ref: '#/definitions/DocResponseSingleKey'
- /v1/{project}/kie/revision/{key_id}:
+ delete:
+ summary: delete key by kv ID.
+ operationId: Delete
+ parameters:
+ - name: project
+ in: path
+ required: true
+ type: string
+ - name: kv_id
+ in: path
+ required: true
+ type: string
+ consumes:
+ - '*/*'
+ produces:
+ - '*/*'
+ responses:
+ "204":
+ description: delete success
+ "404":
+ description: no key value found for deletion
+ "500":
+ description: server error
+ /v1/{project}/kie/revision/{kv_id}:
get:
summary: get all revisions by key id
operationId: GetRevisions
@@ -199,7 +190,7 @@ paths:
in: path
required: true
type: string
- - name: key_id
+ - name: kv_id
in: path
required: true
type: string
@@ -213,9 +204,7 @@ paths:
"200":
description: ""
schema:
- type: array
- items:
- $ref: '#/definitions/DocResponseSingleKey'
+ $ref: '#/definitions/DocResponseGetKey'
/v1/{project}/kie/track:
get:
summary: get polling tracks of clients of kie server
@@ -355,14 +344,25 @@ definitions:
type: string
user_agent:
type: string
- v1.KVBody:
+ v1.KVCreateBody:
type: object
properties:
+ key:
+ type: string
labels:
type: object
additionalProperties:
type: string
+ status:
+ type: string
value:
type: string
value_type:
type: string
+ v1.KVUpdateBody:
+ type: object
+ properties:
+ status:
+ type: string
+ value:
+ type: string
diff --git a/pkg/common/common.go b/pkg/common/common.go
index 82449c3..097a2b1 100644
--- a/pkg/common/common.go
+++ b/pkg/common/common.go
@@ -21,16 +21,18 @@ import "time"
//match mode
const (
- QueryParamQ = "q"
- QueryByLabelsCon = "&"
- QueryParamWait = "wait"
- QueryParamRev = "revision"
- QueryParamMatch = "match"
- QueryParamKeyID = "kv_id"
- QueryParamLabel = "label"
- QueryParamStatus = "status"
- QueryParamOffset = "offset"
- QueryParamLimit = "limit"
+ QueryParamQ = "q"
+ QueryByLabelsCon = "&"
+ QueryParamWait = "wait"
+ QueryParamRev = "revision"
+ QueryParamMatch = "match"
+ QueryParamKey = "key"
+ QueryParamLabel = "label"
+ QueryParamStatus = "status"
+ QueryParamOffset = "offset"
+ QueryParamLimit = "limit"
+ PathParamKVID = "kv_id"
+ PathParameterProject = "project"
//polling data
QueryParamSessionID = "sessionId"
QueryParamIP = "ip"
@@ -59,13 +61,13 @@ const (
StatusEnabled = "enabled"
StatusDisabled = "disabled"
MsgDomainMustNotBeEmpty = "domain must not be empty"
+ MsgDeleteKVFailed = "delete kv failed"
MsgIllegalLabels = "label value can not be empty, " +
"label can not be duplicated, please check query parameters"
- MsgIllegalDepth = "X-Depth must be number"
- MsgInvalidWait = "wait param should be formed with number and time
unit like 5s,100ms, and less than 5m"
- MsgInvalidRev = "revision param should be formed with number
greater than 0"
- ErrKvIDMustNotEmpty = "must supply kv id if you want to remove key"
- RespBodyContextKey = "responseBody"
+ MsgIllegalDepth = "X-Depth must be number"
+ MsgInvalidWait = "wait param should be formed with number and time
unit like 5s,100ms, and less than 5m"
+ MsgInvalidRev = "revision param should be formed with number
greater than 0"
+ RespBodyContextKey = "responseBody"
MaxWait = 5 * time.Minute
)
diff --git a/server/handler/track_handler.go b/server/handler/track_handler.go
index 1fc1122..88028d7 100644
--- a/server/handler/track_handler.go
+++ b/server/handler/track_handler.go
@@ -77,7 +77,7 @@ func (h *TrackHandler) Handle(chain *handler.Chain, inv
*invocation.Invocation,
data.PollingData = map[string]interface{}{
"revStr": revStr,
"wait": wait,
- "project": req.HeaderParameter(v1.PathParameterProject),
+ "project":
req.HeaderParameter(common.PathParameterProject),
"labels": req.QueryParameter("label"),
}
_, err := track.CreateOrUpdate(inv.Ctx, data)
diff --git a/server/resource/v1/common.go b/server/resource/v1/common.go
index d84ea45..3e1a7ed 100644
--- a/server/resource/v1/common.go
+++ b/server/resource/v1/common.go
@@ -22,6 +22,7 @@ import (
"encoding/json"
"errors"
"github.com/apache/servicecomb-kie/pkg/model"
+ "github.com/apache/servicecomb-kie/server/service/mongo/session"
"net/http"
"strconv"
"strings"
@@ -39,12 +40,9 @@ import (
//const of server
const (
- HeaderUserAgent = "User-Agent"
- HeaderSessionID = "X-Session-Id"
- PathParameterProject = "project"
- PathParameterKey = "key"
- AttributeDomainKey = "domain"
- MsgLabelsNotFound = "can not find by labels"
+ HeaderUserAgent = "User-Agent"
+ HeaderSessionID = "X-Session-Id"
+ AttributeDomainKey = "domain"
)
//err
@@ -212,6 +210,52 @@ func checkPagination(offsetStr, limitStr string) (int64,
int64, error) {
return offset, limit, err
}
+func validatePost(kv *model.KVDoc) error {
+ err := checkDomainAndProject(kv.Domain, kv.Project)
+ if err != nil {
+ return err
+ }
+ if kv.Key == "" {
+ return session.ErrKeyIsNil
+ }
+ _, err = checkStatus(kv.Status)
+ return err
+}
+
+func validatePut(kv *model.KVDoc) error {
+ err := validateGet(kv.Domain, kv.Project, kv.ID)
+ if err != nil {
+ return err
+ }
+ _, err = checkStatus(kv.Status)
+ return err
+}
+
+func validateGet(domain, project, kvID string) error {
+ if kvID == "" {
+ return session.ErrIDIsNil
+ }
+ return checkDomainAndProject(domain, project)
+}
+
+func validateList(domain, project string) error {
+ return checkDomainAndProject(domain, project)
+}
+
+func validateDelete(domain, project, kvID string) error {
+ return validateGet(domain, project, kvID)
+}
+
+func checkDomainAndProject(domain, project string) error {
+ if domain == "" {
+ return session.ErrMissingDomain
+ }
+ if project == "" {
+ return session.ErrMissingProject
+ }
+ return nil
+}
+
func checkStatus(status string) (string, error) {
if status != "" {
if status != common.StatusEnabled && status !=
common.StatusDisabled {
diff --git a/server/resource/v1/doc_struct.go b/server/resource/v1/doc_struct.go
index a56ec08..63f2613 100644
--- a/server/resource/v1/doc_struct.go
+++ b/server/resource/v1/doc_struct.go
@@ -88,11 +88,10 @@ var (
"if it is empty, server will return kv which's labels
partial match the label query param. " +
"uf it is exact, server will return kv which's labels
exact match the label query param",
}
- DocQueryKeyIDParameters = &restful.Parameters{
+ DocQueryKeyParameters = &restful.Parameters{
DataType: "string",
- Name: common.QueryParamKeyID,
+ Name: common.QueryParamKey,
ParamType: goRestful.QueryParameterKind,
- Required: true,
}
DocQueryLabelParameters = &restful.Parameters{
DataType: "string",
@@ -141,31 +140,33 @@ var (
//swagger doc path params
var (
- DocPathKey = &restful.Parameters{
- DataType: "string",
- Name: "key",
- ParamType: goRestful.PathParameterKind,
- Required: true,
- }
DocPathProject = &restful.Parameters{
DataType: "string",
- Name: "project",
+ Name: common.PathParameterProject,
ParamType: goRestful.PathParameterKind,
Required: true,
}
DocPathKeyID = &restful.Parameters{
DataType: "string",
- Name: "key_id",
+ Name: common.PathParamKVID,
ParamType: goRestful.PathParameterKind,
Required: true,
}
)
-//KVBody is open api doc
-type KVBody struct {
+//KVCreateBody is open api doc
+type KVCreateBody struct {
+ Key string `json:"key"`
Labels map[string]string `json:"labels"`
- ValueType string `json:"value_type"`
+ Status string `json:"status"`
Value string `json:"value"`
+ ValueType string `json:"value_type"`
+}
+
+//KVUpdateBody is open api doc
+type KVUpdateBody struct {
+ Status string `json:"status"`
+ Value string `json:"value"`
}
//ErrorMsg is open api doc
diff --git a/server/resource/v1/history_resource.go
b/server/resource/v1/history_resource.go
index 0e0ff97..5c27ee0 100644
--- a/server/resource/v1/history_resource.go
+++ b/server/resource/v1/history_resource.go
@@ -40,7 +40,7 @@ type HistoryResource struct {
//GetRevisions search key only by label
func (r *HistoryResource) GetRevisions(context *restful.Context) {
var err error
- keyID := context.ReadPathParameter("key_id")
+ kvID := context.ReadPathParameter(common.PathParamKVID)
offsetStr := context.ReadQueryParameter(common.QueryParamOffset)
limitStr := context.ReadQueryParameter(common.QueryParamLimit)
offset, limit, err := checkPagination(offsetStr, limitStr)
@@ -48,14 +48,12 @@ func (r *HistoryResource) GetRevisions(context
*restful.Context) {
WriteErrResponse(context, http.StatusBadRequest, err.Error(),
common.ContentTypeText)
return
}
- if keyID == "" {
- openlogging.Error("key id is nil")
- WriteErrResponse(context, http.StatusForbidden, "key_id must
not be empty", common.ContentTypeText)
+ if kvID == "" {
+ openlogging.Error("kv id is nil")
+ WriteErrResponse(context, http.StatusForbidden, "kv_id must not
be empty", common.ContentTypeText)
return
}
- key := context.ReadQueryParameter("key")
- revisions, err := service.HistoryService.GetHistory(context.Ctx, keyID,
- service.WithKey(key),
+ revisions, err := service.HistoryService.GetHistory(context.Ctx, kvID,
service.WithOffset(offset),
service.WithLimit(limit))
if err != nil {
@@ -138,7 +136,7 @@ func (r *HistoryResource) URLPatterns() []restful.Route {
return []restful.Route{
{
Method: http.MethodGet,
- Path: "/v1/{project}/kie/revision/{key_id}",
+ Path: "/v1/{project}/kie/revision/{kv_id}",
ResourceFunc: r.GetRevisions,
FuncDesc: "get all revisions by key id",
Parameters: []*restful.Parameters{
@@ -147,7 +145,7 @@ func (r *HistoryResource) URLPatterns() []restful.Route {
Returns: []*restful.Returns{
{
Code: http.StatusOK,
- Model: []model.DocResponseSingleKey{},
+ Model: model.DocResponseGetKey{},
},
},
Consumes: []string{goRestful.MIME_JSON,
common.ContentTypeYaml},
diff --git a/server/resource/v1/kv_resource.go
b/server/resource/v1/kv_resource.go
index 86ef153..df93981 100644
--- a/server/resource/v1/kv_resource.go
+++ b/server/resource/v1/kv_resource.go
@@ -19,6 +19,7 @@
package v1
import (
+ "context"
"fmt"
"net/http"
@@ -26,6 +27,7 @@ import (
"github.com/apache/servicecomb-kie/pkg/model"
"github.com/apache/servicecomb-kie/server/pubsub"
"github.com/apache/servicecomb-kie/server/service"
+ "github.com/apache/servicecomb-kie/server/service/mongo/session"
goRestful "github.com/emicklei/go-restful"
"github.com/go-chassis/go-chassis/server/restful"
"github.com/go-mesh/openlogging"
@@ -35,29 +37,31 @@ import (
type KVResource struct {
}
-//Put create or update kv
-func (r *KVResource) Put(context *restful.Context) {
+//Post create a kv
+func (r *KVResource) Post(rctx *restful.Context) {
var err error
- key := context.ReadPathParameter(PathParameterKey)
- project := context.ReadPathParameter(PathParameterProject)
+ project := rctx.ReadPathParameter(common.PathParameterProject)
kv := new(model.KVDoc)
- if err = readRequest(context, kv); err != nil {
- WriteErrResponse(context, http.StatusBadRequest, err.Error(),
common.ContentTypeText)
+ if err = readRequest(rctx, kv); err != nil {
+ WriteErrResponse(rctx, http.StatusBadRequest, err.Error(),
common.ContentTypeText)
return
}
- domain := ReadDomain(context)
- kv.Key = key
+ domain := ReadDomain(rctx)
kv.Domain = domain.(string)
kv.Project = project
- _, err = checkStatus(kv.Status)
+ err = validatePost(kv)
if err != nil {
- WriteErrResponse(context, http.StatusInternalServerError,
err.Error(), common.ContentTypeText)
+ WriteErrResponse(rctx, http.StatusBadRequest, err.Error(),
common.ContentTypeText)
return
}
- kv, err = service.KVService.CreateOrUpdate(context.Ctx, kv)
+ kv, err = service.KVService.Create(rctx.Ctx, kv)
if err != nil {
- openlogging.Error(fmt.Sprintf("put [%v] err:%s", kv,
err.Error()))
- WriteErrResponse(context, http.StatusInternalServerError,
err.Error(), common.ContentTypeText)
+ openlogging.Error(fmt.Sprintf("post err:%s", err.Error()))
+ if err == session.ErrKVAlreadyExists {
+ WriteErrResponse(rctx, http.StatusConflict,
err.Error(), common.ContentTypeText)
+ return
+ }
+ WriteErrResponse(rctx, http.StatusInternalServerError, "create
kv failed", common.ContentTypeText)
return
}
err = pubsub.Publish(&pubsub.KVChangeEvent{
@@ -68,65 +72,106 @@ func (r *KVResource) Put(context *restful.Context) {
Action: pubsub.ActionPut,
})
if err != nil {
- openlogging.Warn("lost kv change event:" + err.Error())
+ openlogging.Warn("lost kv change event when post:" +
err.Error())
}
openlogging.Info(
- fmt.Sprintf("put [%s] success", kv.Key))
- err = writeResponse(context, kv)
+ fmt.Sprintf("post [%s] success", kv.ID))
+ err = writeResponse(rctx, kv)
if err != nil {
openlogging.Error(err.Error())
}
}
-//GetByKey search key by label and key
-func (r *KVResource) GetByKey(rctx *restful.Context) {
+//Put update a kv
+func (r *KVResource) Put(rctx *restful.Context) {
var err error
- key := rctx.ReadPathParameter(PathParameterKey)
- if key == "" {
- WriteErrResponse(rctx, http.StatusBadRequest, "key must not be
empty", common.ContentTypeText)
+ kvID := rctx.ReadPathParameter(common.PathParamKVID)
+ project := rctx.ReadPathParameter(common.PathParameterProject)
+ kv := new(model.KVDoc)
+ if err = readRequest(rctx, kv); err != nil {
+ WriteErrResponse(rctx, http.StatusBadRequest, err.Error(),
common.ContentTypeText)
return
}
- project := rctx.ReadPathParameter(PathParameterProject)
- labels, err := getLabels(rctx)
+ domain := ReadDomain(rctx)
+ kv.ID = kvID
+ kv.Domain = domain.(string)
+ kv.Project = project
+ err = validatePut(kv)
if err != nil {
- WriteErrResponse(rctx, http.StatusBadRequest,
common.MsgIllegalLabels, common.ContentTypeText)
+ WriteErrResponse(rctx, http.StatusBadRequest, err.Error(),
common.ContentTypeText)
return
}
- domain := ReadDomain(rctx)
- offsetStr := rctx.ReadQueryParameter(common.QueryParamOffset)
- limitStr := rctx.ReadQueryParameter(common.QueryParamLimit)
- offset, limit, err := checkPagination(offsetStr, limitStr)
+ kv, err = service.KVService.Update(rctx.Ctx, kv)
if err != nil {
- WriteErrResponse(rctx, http.StatusBadRequest, err.Error(),
common.ContentTypeText)
+ openlogging.Error(fmt.Sprintf("put [%s] err:%s", kvID,
err.Error()))
+ WriteErrResponse(rctx, http.StatusInternalServerError, "update
kv failed", common.ContentTypeText)
return
}
- sessionID := rctx.ReadHeader(HeaderSessionID)
- statusStr := rctx.ReadQueryParameter(common.QueryParamStatus)
- status, err := checkStatus(statusStr)
+ err = pubsub.Publish(&pubsub.KVChangeEvent{
+ Key: kv.Key,
+ Labels: kv.Labels,
+ Project: project,
+ DomainID: kv.Domain,
+ Action: pubsub.ActionPut,
+ })
+ if err != nil {
+ openlogging.Warn("lost kv change event when put:" + err.Error())
+ }
+ openlogging.Info(
+ fmt.Sprintf("put [%s] success", kvID))
+ err = writeResponse(rctx, kv)
+ if err != nil {
+ openlogging.Error(err.Error())
+ }
+
+}
+
+//Get search key by kv id
+func (r *KVResource) Get(rctx *restful.Context) {
+ project := rctx.ReadPathParameter(common.PathParameterProject)
+ domain := ReadDomain(rctx).(string)
+ kvID := rctx.ReadPathParameter(common.PathParamKVID)
+ err := validateGet(domain, project, kvID)
if err != nil {
WriteErrResponse(rctx, http.StatusBadRequest, err.Error(),
common.ContentTypeText)
return
}
- returnData(rctx, &model.KVDoc{
- Domain: domain.(string),
- Project: project,
- Key: key,
- Labels: labels,
- Status: status,
- }, offset, limit, sessionID)
+ kv, err := service.KVService.Get(rctx.Ctx, domain, project, kvID)
+ if err != nil {
+ openlogging.Error("kv_resource: " + err.Error())
+ if err == service.ErrKeyNotExists {
+ WriteErrResponse(rctx, http.StatusNotFound,
err.Error(), common.ContentTypeText)
+ return
+ }
+ WriteErrResponse(rctx, http.StatusInternalServerError, "get kv
failed", common.ContentTypeText)
+ return
+ }
+ kv.Domain = ""
+ kv.Project = ""
+ err = writeResponse(rctx, kv)
+ rctx.Ctx = context.WithValue(rctx.Ctx, common.RespBodyContextKey, kv)
+ if err != nil {
+ openlogging.Error(err.Error())
+ }
}
//List response kv list
func (r *KVResource) List(rctx *restful.Context) {
var err error
- project := rctx.ReadPathParameter(PathParameterProject)
- domain := ReadDomain(rctx)
- labels, err := getLabels(rctx)
+ key := rctx.ReadQueryParameter(common.QueryParamKey)
+ project := rctx.ReadPathParameter(common.PathParameterProject)
+ domain := ReadDomain(rctx).(string)
+ err = validateList(domain, project)
if err != nil {
WriteErrResponse(rctx, http.StatusBadRequest, err.Error(),
common.ContentTypeText)
return
}
+ labels, err := getLabels(rctx)
+ if err != nil {
+ WriteErrResponse(rctx, http.StatusBadRequest,
common.MsgIllegalLabels, common.ContentTypeText)
+ return
+ }
offsetStr := rctx.ReadQueryParameter(common.QueryParamOffset)
limitStr := rctx.ReadQueryParameter(common.QueryParamLimit)
@@ -143,8 +188,9 @@ func (r *KVResource) List(rctx *restful.Context) {
return
}
returnData(rctx, &model.KVDoc{
- Domain: domain.(string),
+ Domain: domain,
Project: project,
+ Key: key,
Labels: labels,
Status: status,
}, offset, limit, sessionID)
@@ -210,57 +256,76 @@ func returnData(rctx *restful.Context, doc *model.KVDoc,
offset, limit int64, se
}
//Delete deletes key by ids
-func (r *KVResource) Delete(context *restful.Context) {
- project := context.ReadPathParameter(PathParameterProject)
- domain := ReadDomain(context)
- kvID := context.ReadQueryParameter(common.QueryParamKeyID)
- if kvID == "" {
- WriteErrResponse(context, http.StatusBadRequest,
common.ErrKvIDMustNotEmpty, common.ContentTypeText)
+func (r *KVResource) Delete(rctx *restful.Context) {
+ project := rctx.ReadPathParameter(common.PathParameterProject)
+ domain := ReadDomain(rctx).(string)
+ kvID := rctx.ReadPathParameter(common.PathParamKVID)
+ err := validateDelete(domain, project, kvID)
+ if err != nil {
+ WriteErrResponse(rctx, http.StatusBadRequest, err.Error(),
common.ContentTypeText)
return
}
- result, err := service.KVService.Get(context.Ctx, domain.(string),
project, kvID)
- if err != nil && err != service.ErrKeyNotExists {
- WriteErrResponse(context, http.StatusInternalServerError,
err.Error(), common.ContentTypeText)
- return
- } else if err == service.ErrKeyNotExists {
- context.WriteHeader(http.StatusNoContent)
+ kv, err := service.KVService.Get(rctx.Ctx, domain, project, kvID)
+ if err != nil {
+ openlogging.Error("kv_resource: " + err.Error())
+ if err == service.ErrKeyNotExists {
+ WriteErrResponse(rctx, http.StatusNotFound,
err.Error(), common.ContentTypeText)
+ return
+ }
+ WriteErrResponse(rctx, http.StatusInternalServerError,
common.MsgDeleteKVFailed, common.ContentTypeText)
return
}
- kv := result.Data[0]
- err = service.KVService.Delete(context.Ctx, kvID, domain.(string),
project)
+ err = service.KVService.Delete(rctx.Ctx, kvID, domain, project)
if err != nil {
openlogging.Error("delete failed ,",
openlogging.WithTags(openlogging.Tags{
"kvID": kvID,
"error": err.Error(),
}))
- WriteErrResponse(context, http.StatusInternalServerError,
err.Error(), common.ContentTypeText)
+ WriteErrResponse(rctx, http.StatusInternalServerError,
common.MsgDeleteKVFailed, common.ContentTypeText)
return
}
err = pubsub.Publish(&pubsub.KVChangeEvent{
Key: kv.Key,
Labels: kv.Labels,
Project: project,
- DomainID: domain.(string),
+ DomainID: domain,
Action: pubsub.ActionDelete,
})
if err != nil {
openlogging.Warn("lost kv change event:" + err.Error())
}
- context.WriteHeader(http.StatusNoContent)
+ rctx.WriteHeader(http.StatusNoContent)
}
//URLPatterns defined config operations
func (r *KVResource) URLPatterns() []restful.Route {
return []restful.Route{
{
+ Method: http.MethodPost,
+ Path: "/v1/{project}/kie/kv",
+ ResourceFunc: r.Post,
+ FuncDesc: "create a key value",
+ Parameters: []*restful.Parameters{
+ DocPathProject, DocHeaderContentType,
+ },
+ Read: KVCreateBody{},
+ Returns: []*restful.Returns{
+ {
+ Code: http.StatusOK,
+ Model: model.DocResponseSingleKey{},
+ },
+ },
+ Consumes: []string{goRestful.MIME_JSON,
common.ContentTypeYaml},
+ Produces: []string{goRestful.MIME_JSON,
common.ContentTypeYaml},
+ }, {
Method: http.MethodPut,
- Path: "/v1/{project}/kie/kv/{key}",
+ Path: "/v1/{project}/kie/kv/{kv_id}",
ResourceFunc: r.Put,
- FuncDesc: "create or update key value",
+ FuncDesc: "update a key value",
Parameters: []*restful.Parameters{
- DocPathProject, DocPathKey,
DocHeaderContentType,
+ DocPathProject, DocPathKeyID,
DocHeaderContentType,
},
- Read: KVBody{},
+ Read: KVUpdateBody{},
Returns: []*restful.Returns{
{
Code: http.StatusOK,
@@ -271,25 +336,24 @@ func (r *KVResource) URLPatterns() []restful.Route {
Produces: []string{goRestful.MIME_JSON,
common.ContentTypeYaml},
}, {
Method: http.MethodGet,
- Path: "/v1/{project}/kie/kv/{key}",
- ResourceFunc: r.GetByKey,
- FuncDesc: "get key values by key and labels",
+ Path: "/v1/{project}/kie/kv/{kv_id}",
+ ResourceFunc: r.Get,
+ FuncDesc: "get key values by kv_id",
Parameters: []*restful.Parameters{
- DocPathProject, DocPathKey,
DocQueryLabelParameters, DocQueryWait, DocQueryMatch, DocQueryRev,
- DocQueryLimitParameters,
DocQueryOffsetParameters,
+ DocPathProject, DocPathKeyID,
},
Returns: []*restful.Returns{
{
Code: http.StatusOK,
Message: "get key value success",
- Model: model.DocResponseGetKey{},
+ Model: model.DocResponseSingleKey{},
Headers: map[string]goRestful.Header{
common.HeaderRevision:
DocHeaderRevision,
},
},
{
- Code: http.StatusNotModified,
- Message: "empty body",
+ Code: http.StatusNotFound,
+ Message: "key value not found",
},
},
Produces: []string{goRestful.MIME_JSON,
common.ContentTypeYaml},
@@ -299,7 +363,7 @@ func (r *KVResource) URLPatterns() []restful.Route {
ResourceFunc: r.List,
FuncDesc: "list key values by labels and key",
Parameters: []*restful.Parameters{
- DocPathProject, DocQueryLabelParameters,
DocQueryWait, DocQueryMatch, DocQueryRev,
+ DocPathProject, DocQueryKeyParameters,
DocQueryLabelParameters, DocQueryWait, DocQueryMatch, DocQueryRev,
DocQueryLimitParameters,
DocQueryOffsetParameters,
},
Returns: []*restful.Returns{
@@ -317,21 +381,25 @@ func (r *KVResource) URLPatterns() []restful.Route {
Produces: []string{goRestful.MIME_JSON,
common.ContentTypeYaml},
}, {
Method: http.MethodDelete,
- Path: "/v1/{project}/kie/kv",
+ Path: "/v1/{project}/kie/kv/{kv_id}",
ResourceFunc: r.Delete,
FuncDesc: "delete key by kv ID.",
Parameters: []*restful.Parameters{
DocPathProject,
- DocQueryKeyIDParameters,
+ DocPathKeyID,
},
Returns: []*restful.Returns{
{
Code: http.StatusNoContent,
- Message: "Delete success",
+ Message: "delete success",
+ },
+ {
+ Code: http.StatusNotFound,
+ Message: "no key value found for
deletion",
},
{
Code: http.StatusInternalServerError,
- Message: "Server error",
+ Message: "server error",
},
},
},
diff --git a/server/resource/v1/kv_resource_test.go
b/server/resource/v1/kv_resource_test.go
index fcd789e..6b8f27e 100644
--- a/server/resource/v1/kv_resource_test.go
+++ b/server/resource/v1/kv_resource_test.go
@@ -65,14 +65,15 @@ func init() {
pubsub.Init()
pubsub.Start()
}
-func TestKVResource_Put(t *testing.T) {
- t.Run("put kv, label is service", func(t *testing.T) {
+func TestKVResource_Post(t *testing.T) {
+ t.Run("post kv, label is service", func(t *testing.T) {
kv := &model.KVDoc{
+ Key: "timeout",
Value: "1s",
Labels: map[string]string{"service": "utService"},
}
j, _ := json.Marshal(kv)
- r, _ := http.NewRequest("PUT", "/v1/test/kie/kv/timeout",
bytes.NewBuffer(j))
+ r, _ := http.NewRequest("POST", "/v1/test/kie/kv",
bytes.NewBuffer(j))
noopH := &handler2.NoopAuthHandler{}
chain, _ := handler.CreateChain(common.Provider, "testchain1",
noopH.Name())
r.Header.Set("Content-Type", "application/json")
@@ -90,13 +91,14 @@ func TestKVResource_Put(t *testing.T) {
assert.Equal(t, kv.Value, data.Value)
assert.Equal(t, kv.Labels, data.Labels)
})
- t.Run("put a different key, which label is same to timeout", func(t
*testing.T) {
+ t.Run("post a different key, which label is same to timeout", func(t
*testing.T) {
kv := &model.KVDoc{
+ Key: "interval",
Value: "1s",
Labels: map[string]string{"service": "utService"},
}
j, _ := json.Marshal(kv)
- r, _ := http.NewRequest("PUT", "/v1/test/kie/kv/interval",
bytes.NewBuffer(j))
+ r, _ := http.NewRequest("POST", "/v1/test/kie/kv",
bytes.NewBuffer(j))
noopH := &handler2.NoopAuthHandler{}
chain, _ := handler.CreateChain(common.Provider, "testchain1",
noopH.Name())
r.Header.Set("Content-Type", "application/json")
@@ -114,15 +116,16 @@ func TestKVResource_Put(t *testing.T) {
assert.Equal(t, kv.Value, data.Value)
assert.Equal(t, kv.Labels, data.Labels)
})
- t.Run("put kv,label is service and version", func(t *testing.T) {
+ t.Run("post kv,label is service and version", func(t *testing.T) {
kv := &model.KVDoc{
+ Key: "timeout",
Value: "1s",
Labels: map[string]string{
"service": "utService",
"version": "1.0.0"},
}
j, _ := json.Marshal(kv)
- r, _ := http.NewRequest("PUT", "/v1/test/kie/kv/timeout",
bytes.NewBuffer(j))
+ r, _ := http.NewRequest("POST", "/v1/test/kie/kv",
bytes.NewBuffer(j))
noopH := &handler2.NoopAuthHandler{}
chain, _ := handler.CreateChain(common.Provider, "testchain1",
noopH.Name())
r.Header.Set("Content-Type", "application/json")
@@ -277,11 +280,12 @@ func TestKVResource_List(t *testing.T) {
wg.Add(1)
go func() {
kv := &model.KVDoc{
+ Key: "testKey",
Value: "val",
Labels: map[string]string{"dummy": "test",
"match": "test"},
}
j, _ := json.Marshal(kv)
- r2, _ := http.NewRequest("PUT",
"/v1/test/kie/kv/testKey", bytes.NewBuffer(j))
+ r2, _ := http.NewRequest("POST", "/v1/test/kie/kv",
bytes.NewBuffer(j))
noopH2 := &handler2.NoopAuthHandler{}
chain2, _ := handler.CreateChain(common.Provider,
"testchain-match", noopH2.Name())
r2.Header.Set("Content-Type", "application/json")
@@ -305,10 +309,8 @@ func TestKVResource_List(t *testing.T) {
assert.Equal(t, 304, resp.Code)
t.Log(duration)
})
-}
-func TestKVResource_GetByKey(t *testing.T) {
t.Run("get one key by label, exact match,should return 1 kv", func(t
*testing.T) {
- r, _ := http.NewRequest("GET",
"/v1/test/kie/kv/timeout?label=service:utService&match=exact", nil)
+ r, _ := http.NewRequest("GET",
"/v1/test/kie/kv?key=timeout&label=service:utService&match=exact", nil)
noopH := &handler2.NoopAuthHandler{}
chain, _ := handler.CreateChain(common.Provider, "testchain1",
noopH.Name())
r.Header.Set("Content-Type", "application/json")
@@ -325,7 +327,7 @@ func TestKVResource_GetByKey(t *testing.T) {
assert.Equal(t, 1, len(result.Data))
})
t.Run("get one key by service label should return 2 kv,delete one",
func(t *testing.T) {
- r, _ := http.NewRequest("GET",
"/v1/test/kie/kv/timeout?label=service:utService", nil)
+ r, _ := http.NewRequest("GET",
"/v1/test/kie/kv?key=timeout&label=service:utService", nil)
noopH := &handler2.NoopAuthHandler{}
chain, _ := handler.CreateChain(common.Provider, "testchain1",
noopH.Name())
r.Header.Set("Content-Type", "application/json")
@@ -341,7 +343,7 @@ func TestKVResource_GetByKey(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 2, len(result.Data))
- r2, _ := http.NewRequest("DELETE",
"/v1/test/kie/kv?kv_id="+result.Data[0].ID, nil)
+ r2, _ := http.NewRequest("DELETE",
"/v1/test/kie/kv/"+result.Data[0].ID, nil)
c2, err := restfultest.New(kvr, chain)
assert.NoError(t, err)
resp2 := httptest.NewRecorder()
@@ -350,3 +352,86 @@ func TestKVResource_GetByKey(t *testing.T) {
})
}
+func TestKVResource_PutAndGet(t *testing.T) {
+ var id string
+ kv := &model.KVDoc{
+ Key: "user",
+ Value: "guest",
+ Labels: map[string]string{"service": "utService"},
+ }
+ t.Run("create a kv, the value of user is guest", func(t *testing.T) {
+ j, _ := json.Marshal(kv)
+ r, _ := http.NewRequest("POST", "/v1/test/kie/kv",
bytes.NewBuffer(j))
+ noopH := &handler2.NoopAuthHandler{}
+ chain, _ := handler.CreateChain(common.Provider, "testchain1",
noopH.Name())
+ r.Header.Set("Content-Type", "application/json")
+ kvr := &v1.KVResource{}
+ c, _ := restfultest.New(kvr, chain)
+ resp := httptest.NewRecorder()
+ c.ServeHTTP(resp, r)
+
+ body, err := ioutil.ReadAll(resp.Body)
+ assert.NoError(t, err)
+ result := &model.KVDoc{}
+ err = json.Unmarshal(body, result)
+ assert.NoError(t, err)
+ assert.NotEmpty(t, result.ID)
+ assert.Equal(t, kv.Value, result.Value)
+ id = result.ID
+ })
+ t.Run("get one key by kv_id", func(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/v1/test/kie/kv/"+id, nil)
+ noopH := &handler2.NoopAuthHandler{}
+ chain, _ := handler.CreateChain(common.Provider, "testchain1",
noopH.Name())
+ r.Header.Set("Content-Type", "application/json")
+ kvr := &v1.KVResource{}
+ c, err := restfultest.New(kvr, chain)
+ assert.NoError(t, err)
+ resp := httptest.NewRecorder()
+ c.ServeHTTP(resp, r)
+ body, err := ioutil.ReadAll(resp.Body)
+ assert.NoError(t, err)
+ result := &model.KVDoc{}
+ err = json.Unmarshal(body, result)
+ assert.NoError(t, err)
+ assert.Equal(t, kv.Value, result.Value)
+ })
+ kvUpdate := &model.KVDoc{
+ Value: "admin",
+ }
+ t.Run("update the kv, set the value of user to admin", func(t
*testing.T) {
+ j, _ := json.Marshal(kvUpdate)
+ r, _ := http.NewRequest("PUT", "/v1/test/kie/kv/"+id,
bytes.NewBuffer(j))
+ noopH := &handler2.NoopAuthHandler{}
+ chain, _ := handler.CreateChain(common.Provider, "testchain1",
noopH.Name())
+ r.Header.Set("Content-Type", "application/json")
+ kvr := &v1.KVResource{}
+ c, _ := restfultest.New(kvr, chain)
+ resp := httptest.NewRecorder()
+ c.ServeHTTP(resp, r)
+
+ body, err := ioutil.ReadAll(resp.Body)
+ assert.NoError(t, err)
+ result := &model.KVDoc{}
+ err = json.Unmarshal(body, result)
+ assert.NoError(t, err)
+ assert.Equal(t, kvUpdate.Value, result.Value)
+ })
+ t.Run("get one key by kv_id again", func(t *testing.T) {
+ r, _ := http.NewRequest("GET", "/v1/test/kie/kv/"+id, nil)
+ noopH := &handler2.NoopAuthHandler{}
+ chain, _ := handler.CreateChain(common.Provider, "testchain1",
noopH.Name())
+ r.Header.Set("Content-Type", "application/json")
+ kvr := &v1.KVResource{}
+ c, err := restfultest.New(kvr, chain)
+ assert.NoError(t, err)
+ resp := httptest.NewRecorder()
+ c.ServeHTTP(resp, r)
+ body, err := ioutil.ReadAll(resp.Body)
+ assert.NoError(t, err)
+ result := &model.KVDoc{}
+ err = json.Unmarshal(body, result)
+ assert.NoError(t, err)
+ assert.Equal(t, kvUpdate.Value, result.Value)
+ })
+}
diff --git a/server/service/mongo/kv/kv_service.go
b/server/service/mongo/kv/kv_service.go
index 5b01e0b..7bf2011 100644
--- a/server/service/mongo/kv/kv_service.go
+++ b/server/service/mongo/kv/kv_service.go
@@ -32,12 +32,10 @@ import (
//const of kv service
const (
- MsgFindKvFailed = "find kv failed, deadline exceeded"
- MsgFindOneKey = "find one key"
- MsgFindOneKeyByID = "find one key by id"
- MsgFindMoreKey = "find more"
- MsgHitExactLabels = "hit exact labels"
- FmtErrFindKvFailed = "can not find kv in %s"
+ MsgFindKvFailed = "find kv failed, deadline exceeded"
+ MsgFindOneKey = "find one key"
+ MsgCreateLabelFailed = "create label failed"
+ FmtErrFindKvFailed = "can not find kv in %s"
)
//Service operate data in mongodb
@@ -45,6 +43,70 @@ type Service struct {
timeout time.Duration
}
+//Create will create a key value record
+func (s *Service) Create(ctx context.Context, kv *model.KVDoc) (*model.KVDoc,
error) {
+ ctx, _ = context.WithTimeout(ctx, session.Timeout)
+ //check whether the project has certain labels or not
+ labelID, err := label.Exist(ctx, kv.Domain, kv.Project, kv.Labels)
+ if err != nil {
+ if err == session.ErrLabelNotExists {
+ l := &model.LabelDoc{
+ Domain: kv.Domain,
+ Labels: kv.Labels,
+ Project: kv.Project,
+ }
+ l, err = label.CreateLabel(ctx, l)
+ if err != nil {
+ openlogging.Error(MsgCreateLabelFailed,
openlogging.WithTags(openlogging.Tags{
+ "k": kv.Key,
+ "domain": kv.Domain,
+ }))
+ return nil, err
+ }
+ labelID = l.ID
+ } else {
+ return nil, err
+ }
+ }
+ kv.LabelID = labelID
+ if kv.ValueType == "" {
+ kv.ValueType = session.DefaultValueType
+ }
+ _, err = s.Exist(ctx, kv.Domain, kv.Key, kv.Project,
service.WithLabelID(kv.LabelID))
+ if err == nil {
+ return nil, session.ErrKVAlreadyExists
+ }
+ if err != service.ErrKeyNotExists {
+ openlogging.Error(err.Error())
+ return nil, err
+ }
+ kv, err = createKey(ctx, kv)
+ if err != nil {
+ openlogging.Error(err.Error())
+ return nil, err
+ }
+ clearPart(kv)
+ return kv, nil
+}
+
+//Update will update a key value record
+func (s *Service) Update(ctx context.Context, kv *model.KVDoc) (*model.KVDoc,
error) {
+ ctx, _ = context.WithTimeout(ctx, session.Timeout)
+ oldKV, err := s.Get(ctx, kv.Domain, kv.Project, kv.ID)
+ if err != nil {
+ return nil, err
+ }
+ oldKV.Status = kv.Status
+ oldKV.Value = kv.Value
+ err = updateKeyValue(ctx, oldKV)
+ if err != nil {
+ return nil, err
+ }
+ clearPart(oldKV)
+ return oldKV, nil
+
+}
+
//CreateOrUpdate will create or update a key value record
//it first check label exists or not, and create labels if labels is first
posted.
//if label exists, then get its latest revision, and update current revision,
@@ -66,7 +128,7 @@ func (s *Service) CreateOrUpdate(ctx context.Context, kv
*model.KVDoc) (*model.K
}
l, err = label.CreateLabel(ctx, l)
if err != nil {
- openlogging.Error("create label failed",
openlogging.WithTags(openlogging.Tags{
+ openlogging.Error(MsgCreateLabelFailed,
openlogging.WithTags(openlogging.Tags{
"k": kv.Key,
"domain": kv.Domain,
}))
@@ -143,15 +205,6 @@ func (s *Service) Exist(ctx context.Context, domain, key
string, project string,
//domain=tenant
func (s *Service) Delete(ctx context.Context, kvID string, domain string,
project string) error {
ctx, _ = context.WithTimeout(context.Background(), session.Timeout)
- if domain == "" {
- return session.ErrMissingDomain
- }
- if project == "" {
- return session.ErrMissingProject
- }
- if kvID == "" {
- return errors.New("key id is empty")
- }
//delete kv
err := deleteKV(ctx, kvID, project, domain)
if err != nil {
@@ -194,7 +247,7 @@ func (s *Service) List(ctx context.Context, domain, project
string, options ...s
}
//Get get kvs by id
-func (s *Service) Get(ctx context.Context, domain, project, id string, options
...service.FindOption) (*model.KVResponse, error) {
+func (s *Service) Get(ctx context.Context, domain, project, id string, options
...service.FindOption) (*model.KVDoc, error) {
opts := service.FindOptions{}
for _, o := range options {
o(&opts)
@@ -202,14 +255,5 @@ func (s *Service) Get(ctx context.Context, domain,
project, id string, options .
if opts.Timeout == 0 {
opts.Timeout = session.DefaultTimeout
}
- if domain == "" {
- return nil, session.ErrMissingDomain
- }
- if project == "" {
- return nil, session.ErrMissingProject
- }
- if id == "" {
- return nil, session.ErrIDIsNil
- }
- return findKVByID(ctx, domain, project, id)
+ return findKVDocByID(ctx, domain, project, id)
}
diff --git a/server/service/mongo/kv/kv_test.go
b/server/service/mongo/kv/kv_test.go
index f43888d..236d8ad 100644
--- a/server/service/mongo/kv/kv_test.go
+++ b/server/service/mongo/kv/kv_test.go
@@ -28,7 +28,8 @@ import (
"testing"
)
-//
+var id string
+
func TestService_CreateOrUpdate(t *testing.T) {
var err error
config.Configurations = &config.Config{DB: config.DB{URI:
"mongodb://kie:[email protected]:27017/kie"}}
@@ -101,30 +102,57 @@ func TestService_CreateOrUpdate(t *testing.T) {
}
-func TestService_Delete(t *testing.T) {
+func TestService_Create(t *testing.T) {
kvsvc := &kv.Service{}
- t.Run("delete key by kvID", func(t *testing.T) {
- kv1, err := kvsvc.CreateOrUpdate(context.Background(),
&model.KVDoc{
+ t.Run("create kv timeout,with labels app and service", func(t
*testing.T) {
+ result, err := kvsvc.Create(context.TODO(), &model.KVDoc{
Key: "timeout",
- Value: "20s",
+ Value: "2s",
Labels: map[string]string{
- "env": "test",
+ "app": "mall",
+ "service": "utCart",
},
Domain: "default",
Project: "kv-test",
})
assert.NoError(t, err)
+ assert.NotEmpty(t, result.ID)
+ assert.Equal(t, "2s", result.Value)
+ id = result.ID
+ })
+ t.Run("create the same kv", func(t *testing.T) {
+ _, err := kvsvc.Create(context.TODO(), &model.KVDoc{
+ Key: "timeout",
+ Value: "2s",
+ Labels: map[string]string{
+ "app": "mall",
+ "service": "utCart",
+ },
+ Domain: "default",
+ Project: "kv-test",
+ })
+ assert.EqualError(t, err, session.ErrKVAlreadyExists.Error())
+ })
+}
- err = kvsvc.Delete(context.TODO(), kv1.ID, "default", "kv-test")
+func TestService_Update(t *testing.T) {
+ kvsvc := &kv.Service{}
+ t.Run("update kv by kvID", func(t *testing.T) {
+ result, err := kvsvc.Update(context.TODO(), &model.KVDoc{
+ ID: id,
+ Value: "3s",
+ Domain: "default",
+ Project: "kv-test",
+ })
assert.NoError(t, err)
-
+ assert.Equal(t, "3s", result.Value)
})
- t.Run("miss id", func(t *testing.T) {
- err := kvsvc.Delete(context.TODO(), "", "default", "kv-test")
- assert.Error(t, err)
- })
- t.Run("miss domain", func(t *testing.T) {
- err := kvsvc.Delete(context.TODO(), "2", "", "kv-test")
- assert.Equal(t, session.ErrMissingDomain, err)
+}
+
+func TestService_Delete(t *testing.T) {
+ kvsvc := &kv.Service{}
+ t.Run("delete kv by kvID", func(t *testing.T) {
+ err := kvsvc.Delete(context.TODO(), id, "default", "kv-test")
+ assert.NoError(t, err)
})
}
diff --git a/server/service/mongo/kv/tool.go b/server/service/mongo/kv/tool.go
index de9d0e3..776eea6 100644
--- a/server/service/mongo/kv/tool.go
+++ b/server/service/mongo/kv/tool.go
@@ -18,28 +18,11 @@
package kv
import (
- "context"
"github.com/apache/servicecomb-kie/pkg/model"
)
-//clearAll clean attr which don't need to return to client side
-func clearAll(kv *model.KVDoc) {
- clearPart(kv)
- kv.Labels = nil
- kv.LabelID = ""
-}
+//clearPart remove domain and project of kv
func clearPart(kv *model.KVDoc) {
kv.Domain = ""
kv.Project = ""
}
-
-func findKVByID(ctx context.Context, domain, project, kvID string)
(*model.KVResponse, error) {
- kv, err := findKVDocByID(ctx, domain, project, kvID)
- if err != nil {
- return nil, err
- }
- return &model.KVResponse{
- Total: 1,
- Data: []*model.KVDoc{kv},
- }, nil
-}
diff --git a/server/service/mongo/session/session.go
b/server/service/mongo/session/session.go
index 48b5da2..0446c30 100644
--- a/server/service/mongo/session/session.go
+++ b/server/service/mongo/session/session.go
@@ -62,8 +62,10 @@ var (
ErrKeyMustNotEmpty = errors.New("must supply key if you want to get
exact one result")
ErrIDIsNil = errors.New("id is empty")
+ ErrKeyIsNil = errors.New("key must not be empty")
ErrKvIDAndLabelIDNotMatch = errors.New("kvID and labelID do not match")
ErrRootCAMissing = errors.New("rootCAFile is empty in config
file")
+ ErrKVAlreadyExists = errors.New("kv already exists")
ErrViewCreation = errors.New("can not create view")
ErrViewUpdate = errors.New("can not update view")
diff --git a/server/service/service.go b/server/service/service.go
index d7fa613..476486b 100644
--- a/server/service/service.go
+++ b/server/service/service.go
@@ -43,11 +43,13 @@ var (
//KV provide api of KV entity
type KV interface {
//below 3 methods is usually for admin console
+ Create(ctx context.Context, kv *model.KVDoc) (*model.KVDoc, error)
+ Update(ctx context.Context, kv *model.KVDoc) (*model.KVDoc, error)
CreateOrUpdate(ctx context.Context, kv *model.KVDoc) (*model.KVDoc,
error)
List(ctx context.Context, domain, project string, options
...FindOption) (*model.KVResponse, error)
Delete(ctx context.Context, kvID string, domain, project string) error
//Get return kv by id
- Get(ctx context.Context, domain, project, id string, options
...FindOption) (*model.KVResponse, error)
+ Get(ctx context.Context, domain, project, id string, options
...FindOption) (*model.KVDoc, error)
}
//History provide api of History entity