#78 #80 add status and create/update time (#91)

* add status and create/update time

* modify as comment
diff --git a/deployments/db.js b/deployments/db.js
index ef6e407..1fa2520 100644
--- a/deployments/db.js
+++ b/deployments/db.js
@@ -42,7 +42,16 @@
                 },
                 labels: {
                     bsonType: "object"
-                }
+                },
+                create_time: {
+                    bsonType: "string",
+                },
+                update_time: {
+                    bsonType: "string",
+                },
+                status: {
+                    bsonType: "string",
+                },
             }
         } }
 } );
diff --git a/go.sum b/go.sum
index 7880f67..96a71c3 100644
--- a/go.sum
+++ b/go.sum
@@ -178,8 +178,10 @@
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
@@ -368,6 +370,7 @@
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
diff --git a/pkg/common/common.go b/pkg/common/common.go
index 7d5a4e1..a6bd6bb 100644
--- a/pkg/common/common.go
+++ b/pkg/common/common.go
@@ -46,9 +46,11 @@
 	ContentTypeYaml = "text/yaml"
 )
 
-//const for server/resource/v1
+//const of server
 const (
 	PatternExact            = "exact"
+	StatusEnabled           = "enabled"
+	StatusDisabled          = "disabled"
 	MsgDomainMustNotBeEmpty = "domain must not be empty"
 	MsgIllegalLabels        = "label value can not be empty, " +
 		"label can not be duplicated, please check query parameters"
diff --git a/pkg/model/db_schema.go b/pkg/model/db_schema.go
index 19922f4..950e0cd 100644
--- a/pkg/model/db_schema.go
+++ b/pkg/model/db_schema.go
@@ -38,6 +38,9 @@
 	CreateRevision int64  `json:"create_revision,omitempty" bson:"create_revision," yaml:"create_revision,omitempty"`
 	UpdateRevision int64  `json:"update_revision,omitempty" bson:"update_revision," yaml:"update_revision,omitempty"`
 	Project        string `json:"project,omitempty" yaml:"project,omitempty"`
+	Status         string `json:"status,omitempty" yaml:"status,omitempty"`
+	CreatTime      string `json:"create_time,omitempty" yaml:"create_time,omitempty"`
+	UpdateTime     string `json:"update_time,omitempty" yaml:"update_time,omitempty"`
 
 	Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"` //redundant
 	Domain string            `json:"domain,omitempty" yaml:"domain,omitempty"` //redundant
diff --git a/server/resource/v1/common.go b/server/resource/v1/common.go
index 730d233..99722a8 100644
--- a/server/resource/v1/common.go
+++ b/server/resource/v1/common.go
@@ -36,8 +36,6 @@
 	"gopkg.in/yaml.v2"
 )
 
-//const of server
-
 //err
 var (
 	ErrInvalidRev = errors.New(common.MsgInvalidRev)
@@ -200,8 +198,18 @@
 	}
 	return limit, offset, err
 }
+
+func checkStatus(status string) (string, error) {
+	if status != "" {
+		if status != common.StatusEnabled && status != common.StatusDisabled {
+			return "", errors.New("invalid status string")
+		}
+	}
+	return status, nil
+}
+
 func queryAndResponse(rctx *restful.Context,
-	domain interface{}, project string, key string, labels map[string]string, limit, offset int64) {
+	domain interface{}, project string, key string, labels map[string]string, limit, offset int64, status string) {
 	m := getMatchPattern(rctx)
 	opts := []service.FindOption{
 		service.WithKey(key),
@@ -212,6 +220,9 @@
 	if m == common.PatternExact {
 		opts = append(opts, service.WithExactLabels())
 	}
+	if status != "" {
+		opts = append(opts, service.WithStatus(status))
+	}
 	kv, err := service.KVService.List(rctx.Ctx, domain.(string), project, opts...)
 	if err != nil {
 		if err == service.ErrKeyNotExists {
diff --git a/server/resource/v1/kv_resource.go b/server/resource/v1/kv_resource.go
index 7c7872a..b006a31 100644
--- a/server/resource/v1/kv_resource.go
+++ b/server/resource/v1/kv_resource.go
@@ -52,6 +52,11 @@
 	kv.Key = key
 	kv.Domain = domain.(string)
 	kv.Project = project
+	_, err = checkStatus(kv.Status)
+	if err != nil {
+		WriteErrResponse(context, http.StatusInternalServerError, err.Error(), common.ContentTypeText)
+		return
+	}
 	kv, err = service.KVService.CreateOrUpdate(context.Ctx, kv)
 	if err != nil {
 		openlogging.Error(fmt.Sprintf("put [%v] err:%s", kv, err.Error()))
@@ -103,7 +108,13 @@
 		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
 		return
 	}
-	returnData(rctx, domain, project, labels, limit, offset)
+	statusStr := rctx.ReadQueryParameter("status")
+	status, err := checkStatus(statusStr)
+	if err != nil {
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		return
+	}
+	returnData(rctx, domain, project, labels, limit, offset, status)
 }
 
 //List response kv list
@@ -127,15 +138,21 @@
 		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
 		return
 	}
-	returnData(rctx, domain, project, labels, limit, offset)
+	statusStr := rctx.ReadQueryParameter("status")
+	status, err := checkStatus(statusStr)
+	if err != nil {
+		WriteErrResponse(rctx, http.StatusBadRequest, err.Error(), common.ContentTypeText)
+		return
+	}
+	returnData(rctx, domain, project, labels, limit, offset, status)
 }
 
-func returnData(rctx *restful.Context, domain interface{}, project string, labels map[string]string, limit, offset int64) {
+func returnData(rctx *restful.Context, domain interface{}, project string, labels map[string]string, limit, offset int64, status string) {
 	revStr := rctx.ReadQueryParameter(common.QueryParamRev)
 	wait := rctx.ReadQueryParameter(common.QueryParamWait)
 	if revStr == "" {
 		if wait == "" {
-			queryAndResponse(rctx, domain, project, "", labels, limit, offset)
+			queryAndResponse(rctx, domain, project, "", labels, limit, offset, status)
 			return
 		}
 		changed, err := eventHappened(rctx, wait, &pubsub.Topic{
@@ -149,7 +166,7 @@
 			return
 		}
 		if changed {
-			queryAndResponse(rctx, domain, project, "", labels, limit, offset)
+			queryAndResponse(rctx, domain, project, "", labels, limit, offset, status)
 			return
 		}
 		rctx.WriteHeader(http.StatusNotModified)
@@ -164,7 +181,7 @@
 			return
 		}
 		if revised {
-			queryAndResponse(rctx, domain, project, "", labels, limit, offset)
+			queryAndResponse(rctx, domain, project, "", labels, limit, offset, status)
 			return
 		} else if wait != "" {
 			changed, err := eventHappened(rctx, wait, &pubsub.Topic{
@@ -178,7 +195,7 @@
 				return
 			}
 			if changed {
-				queryAndResponse(rctx, domain, project, "", labels, limit, offset)
+				queryAndResponse(rctx, domain, project, "", labels, limit, offset, status)
 				return
 			}
 			rctx.WriteHeader(http.StatusNotModified)
diff --git a/server/service/mongo/kv/kv_dao.go b/server/service/mongo/kv/kv_dao.go
index 87656de..97cf51f 100644
--- a/server/service/mongo/kv/kv_dao.go
+++ b/server/service/mongo/kv/kv_dao.go
@@ -30,6 +30,7 @@
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/mongo"
 	"go.mongodb.org/mongo-driver/mongo/options"
+	"time"
 )
 
 //createKey get latest revision from history
@@ -46,6 +47,8 @@
 	}
 	kv.UpdateRevision = revision
 	kv.CreateRevision = revision
+	kv.CreatTime = time.Now().String()
+	kv.UpdateTime = time.Now().String()
 	_, err = collection.InsertOne(ctx, kv)
 	if err != nil {
 		openlogging.Error("create error", openlogging.WithTags(openlogging.Tags{
@@ -69,6 +72,7 @@
 //updateKeyValue update key value and add new revision
 func updateKeyValue(ctx context.Context, kv *model.KVDoc) error {
 	var err error
+	kv.UpdateTime = time.Now().String()
 	kv.UpdateRevision, err = counter.ApplyRevision(ctx, kv.Domain)
 	if err != nil {
 		return err
@@ -119,6 +123,9 @@
 	if opts.Offset != 0 {
 		opt = opt.SetSkip(opts.Offset)
 	}
+	if opts.Status != "" {
+		filter["status"] = opts.Status
+	}
 	cur, err := collection.Find(ctx, filter, opt)
 	if err != nil {
 		if err.Error() == context.DeadlineExceeded.Error() {
diff --git a/server/service/options.go b/server/service/options.go
index bae00a7..5022d9f 100644
--- a/server/service/options.go
+++ b/server/service/options.go
@@ -32,6 +32,7 @@
 //FindOptions is option to find key value
 type FindOptions struct {
 	ExactLabels bool
+	Status      string
 	Depth       int
 	Key         string
 	Labels      map[string]string
@@ -59,6 +60,13 @@
 	}
 }
 
+//WithStatus enabled/disabled
+func WithStatus(status string) FindOption {
+	return func(o *FindOptions) {
+		o.Status = status
+	}
+}
+
 //WithTimeout will return err if execution take too long
 func WithTimeout(d time.Duration) FindOption {
 	return func(o *FindOptions) {