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 78341650e6617d4fd97df6feec86c0918f21772b Author: Shawn <[email protected]> AuthorDate: Mon May 13 10:00:45 2019 +0800 add rest api for put and find, (#3) add example for local dev --- .gitignore | 4 +- README.md | 6 +- cmd/kie/cmd.go | 63 ++++++ server/kv/model_suite_test.go => cmd/kie/main.go | 43 ++-- examples/dev/REAMDME.md | 27 +++ examples/dev/conf/chassis.yaml | 24 ++ examples/dev/conf/microservice.yaml | 4 + examples/dev/docker-compose.yaml | 35 +++ examples/dev/kie-conf.yaml | 7 + go.mod | 5 +- server/{kv => dao}/errors.go | 2 +- server/{kv => dao}/kv.go | 9 +- server/{kv => dao}/kv_test.go | 30 +-- server/{kv => dao}/model_suite_test.go | 2 +- server/{kv => dao}/mongodb.go | 8 +- server/{kv => dao}/options.go | 18 +- .../noop_auth_handler.go} | 39 ++-- server/resource/v1/common.go | 60 +++++ server/{kv/errors.go => resource/v1/doc_struct.go} | 23 +- server/resource/v1/kv_resource.go | 249 +++++++++++++++++++++ 20 files changed, 563 insertions(+), 95 deletions(-) diff --git a/.gitignore b/.gitignore index 901a1e4..fcf40a7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,12 +10,10 @@ output **/*junit.xml **/*.exe **/*.tgz - vendor/** !vendor/manifest -# for local UT -**/conf/ +**/conf/servicecomb-kie/ !etc/conf/ etc/data/ etc/ssl/ diff --git a/README.md b/README.md index 244cf63..c427b6a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Apache-ServiceComb-Kie -A service for key value management in distributed system. +A service for configuration management in distributed system. ## Conceptions @@ -63,6 +63,10 @@ to a distributed system in separated views. - kv change history: all kv changes is recorded and can be easily roll back by UI ## Quick Start + +## Development +To see how to build a local dev environment, check [here](examples/dev) + ## Contact Bugs: [issues](https://issues.apache.org/jira/browse/SCB) diff --git a/cmd/kie/cmd.go b/cmd/kie/cmd.go new file mode 100644 index 0000000..45ffb60 --- /dev/null +++ b/cmd/kie/cmd.go @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "os" + + "github.com/urfave/cli" +) + +const ( + defaultConfigFile = "/etc/servicecomb-kie/kie-conf.yaml" +) + +//ConfigFromCmd store cmd params +type ConfigFromCmd struct { + ConfigFile string +} + +//Configs is a pointer of struct ConfigFromCmd +var Configs *ConfigFromCmd + +// parseConfigFromCmd +func parseConfigFromCmd(args []string) (err error) { + app := cli.NewApp() + app.HideVersion = true + app.Usage = "servicecomb-kie server cmd line." + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "config", + Usage: "config file, example: --config=kie-conf.yaml", + Destination: &Configs.ConfigFile, + Value: defaultConfigFile, + }, + } + app.Action = func(c *cli.Context) error { + return nil + } + + err = app.Run(args) + return +} + +//Init get config and parses those command +func Init() error { + Configs = &ConfigFromCmd{} + return parseConfigFromCmd(os.Args) +} diff --git a/server/kv/model_suite_test.go b/cmd/kie/main.go similarity index 56% copy from server/kv/model_suite_test.go copy to cmd/kie/main.go index 965802e..a70ab9e 100644 --- a/server/kv/model_suite_test.go +++ b/cmd/kie/main.go @@ -15,30 +15,33 @@ * limitations under the License. */ -package kv_test +package main import ( - "testing" + _ "github.com/apache/servicecomb-kie/server/handler" - "github.com/go-chassis/paas-lager" + "github.com/apache/servicecomb-kie/server/config" + "github.com/apache/servicecomb-kie/server/resource/v1" + "github.com/go-chassis/go-chassis" "github.com/go-mesh/openlogging" - . "github.com/onsi/ginkgo" - "github.com/onsi/ginkgo/reporters" - . "github.com/onsi/gomega" + "os" ) -func TestModel(t *testing.T) { - RegisterFailHandler(Fail) - junitReporter := reporters.NewJUnitReporter("junit.xml") - RunSpecsWithDefaultAndCustomReporters(t, "Model Suite", []Reporter{junitReporter}) +func main() { + if err := Init(); err != nil { + openlogging.Fatal(err.Error()) + } + chassis.RegisterSchema("rest", &v1.KVResource{}) + if err := chassis.Init(); err != nil { + openlogging.Error(err.Error()) + os.Exit(1) + } + if err := config.Init(Configs.ConfigFile); err != nil { + openlogging.Error(err.Error()) + os.Exit(1) + } + if err := chassis.Run(); err != nil { + openlogging.Error("service exit: " + err.Error()) + os.Exit(1) + } } - -var _ = BeforeSuite(func() { - log.Init(log.Config{ - Writers: []string{"stdout"}, - LoggerLevel: "DEBUG", - }) - - logger := log.NewLogger("ut") - openlogging.SetLogger(logger) -}) diff --git a/examples/dev/REAMDME.md b/examples/dev/REAMDME.md new file mode 100644 index 0000000..5b770d1 --- /dev/null +++ b/examples/dev/REAMDME.md @@ -0,0 +1,27 @@ +# Intro +that is a simple example to run kie in you local machine + +you only need to set up a mongodb and config credential +and related info in kie-conf.yaml + +you can setup a simple mongodb alone with admin UI by docker compose + +# Get started + +1.Build +```bash +cd examples/dev +go build github.com/apache/servicecomb-kie/cmd/kie +``` + +2.Run mongodb and servicecomb-kie +```bash +sudo docker-compose up +./kie --config kie-conf.yaml +``` + +3. check service API document +```bash +cd examples/dev/conf/servicecomb-kie/schema +``` +you can copy it to https://editor.swagger.io/ to see full API document diff --git a/examples/dev/conf/chassis.yaml b/examples/dev/conf/chassis.yaml new file mode 100755 index 0000000..d3d4c4b --- /dev/null +++ b/examples/dev/conf/chassis.yaml @@ -0,0 +1,24 @@ +--- +cse: + service: + registry: + disabled: true + address: http://127.0.0.1:30100 + protocols: + rest: + listenAddress: 127.0.0.1:30108 + rest-consul: #consul compatible API + listenAddress: 127.0.0.1:8500 + handler: + chain: + Provider: + default: auth-handler,ratelimiter-provider +# ssl: +# Provider.cipherPlugin: default +# Provider.verifyPeer: false +# Provider.cipherSuits: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +# Provider.protocol: TLSv1.2 +# Provider.caFile: +# Provider.certFile: +# Provider.keyFile: +# Provider.certPwdFile: diff --git a/examples/dev/conf/microservice.yaml b/examples/dev/conf/microservice.yaml new file mode 100755 index 0000000..38aad04 --- /dev/null +++ b/examples/dev/conf/microservice.yaml @@ -0,0 +1,4 @@ +--- +service_description: + name: servicecomb-kie + version: 0.0.1 diff --git a/examples/dev/docker-compose.yaml b/examples/dev/docker-compose.yaml new file mode 100644 index 0000000..700fa28 --- /dev/null +++ b/examples/dev/docker-compose.yaml @@ -0,0 +1,35 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- +version: '3.1' +services: + mongo: + image: mongo + restart: always + ports: + - 27017:27017 + environment: + MONGO_INITDB_ROOT_USERNAME: kie + MONGO_INITDB_ROOT_PASSWORD: 123 + + mongo-express: + image: mongo-express + restart: always + ports: + - 8081:8081 + environment: + ME_CONFIG_MONGODB_ADMINUSERNAME: kie + ME_CONFIG_MONGODB_ADMINPASSWORD: 123 \ No newline at end of file diff --git a/examples/dev/kie-conf.yaml b/examples/dev/kie-conf.yaml new file mode 100644 index 0000000..5de5ee0 --- /dev/null +++ b/examples/dev/kie-conf.yaml @@ -0,0 +1,7 @@ +db: + uri: mongodb://admin:[email protected]:27017/kie + type: mongodb + poolSize: 10 + ssl: false + sslCA: + sslCert: \ No newline at end of file diff --git a/go.mod b/go.mod index da3d3eb..368d3a8 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,15 @@ module github.com/apache/servicecomb-kie require ( + github.com/emicklei/go-restful v2.8.0+incompatible github.com/go-chassis/go-archaius v0.14.0 - github.com/go-chassis/go-chassis v1.4.0 // indirect + github.com/go-chassis/go-chassis v1.4.0 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/stretchr/testify v1.2.2 - github.com/urfave/cli v1.20.0 // indirect + github.com/urfave/cli v1.20.0 github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect github.com/xdg/stringprep v1.0.0 // indirect go.mongodb.org/mongo-driver v1.0.0 diff --git a/server/kv/errors.go b/server/dao/errors.go similarity index 99% copy from server/kv/errors.go copy to server/dao/errors.go index 958a015..31074f8 100644 --- a/server/kv/errors.go +++ b/server/dao/errors.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package kv +package dao import ( "errors" diff --git a/server/kv/kv.go b/server/dao/kv.go similarity index 91% rename from server/kv/kv.go rename to server/dao/kv.go index 9513e60..de32cc3 100644 --- a/server/kv/kv.go +++ b/server/dao/kv.go @@ -15,7 +15,8 @@ * limitations under the License. */ -package kv +//package dao is a persis layer of kie +package dao import ( "crypto/tls" @@ -30,13 +31,13 @@ var ErrNotExists = errors.New("key with labels does not exits") var ErrTooMany = errors.New("key with labels should be only one") var ErrKeyMustNotEmpty = errors.New("must supply key if you want to get exact one result") -type Service interface { +type KV interface { CreateOrUpdate(kv *model.KV) (*model.KV, error) //do not use primitive.ObjectID as return to decouple with mongodb, we can afford perf lost Exist(key, domain string, labels model.Labels) (string, error) DeleteByID(id string) error Delete(key, domain string, labels model.Labels) error - Find(domain string, options ...CallOption) ([]*model.KV, error) + Find(domain string, options ...FindOption) ([]*model.KV, error) AddHistory(kv *model.KV) error //RollBack(kv *KV, version string) error } @@ -49,7 +50,7 @@ type Options struct { Timeout time.Duration } -func NewKVService() (Service, error) { +func NewKVService() (KV, error) { opts := Options{ URI: config.GetDB().URI, PoolSize: config.GetDB().PoolSize, diff --git a/server/kv/kv_test.go b/server/dao/kv_test.go similarity index 86% rename from server/kv/kv_test.go rename to server/dao/kv_test.go index 4bdd043..9cb23ad 100644 --- a/server/kv/kv_test.go +++ b/server/dao/kv_test.go @@ -15,21 +15,21 @@ * limitations under the License. */ -package kv_test +package dao_test import ( + "github.com/apache/servicecomb-kie/pkg/model" . "github.com/apache/servicecomb-kie/pkg/model" + "github.com/apache/servicecomb-kie/server/dao" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/apache/servicecomb-kie/pkg/model" - "github.com/apache/servicecomb-kie/server/kv" ) var _ = Describe("Kv mongodb service", func() { - var s kv.Service + var s dao.KV var err error Describe("connecting db", func() { - s, err = kv.NewMongoService(kv.Options{ + s, err = dao.NewMongoService(dao.Options{ URI: "mongodb://kie:[email protected]:27017", }) It("should not return err", func() { @@ -100,9 +100,9 @@ var _ = Describe("Kv mongodb service", func() { It("should not return err", func() { Expect(err).Should(BeNil()) }) - kvs1, err := s.Find("default", kv.WithKey("timeout"), kv.WithLabels(map[string]string{ + kvs1, err := s.Find("default", dao.WithKey("timeout"), dao.WithLabels(map[string]string{ "app": "mall", - }), kv.WithExactLabels()) + }), dao.WithExactLabels()) It("should be 1s", func() { Expect(kvs1[0].Value).Should(Equal(beforeKV.Value)) }) @@ -123,9 +123,9 @@ var _ = Describe("Kv mongodb service", func() { It("should exists", func() { Expect(oid).Should(Equal(beforeKV.ID.Hex())) }) - kvs, err := s.Find("default", kv.WithKey("timeout"), kv.WithLabels(map[string]string{ + kvs, err := s.Find("default", dao.WithKey("timeout"), dao.WithLabels(map[string]string{ "app": "mall", - }), kv.WithExactLabels()) + }), dao.WithExactLabels()) It("should be 3s", func() { Expect(kvs[0].Value).Should(Equal(afterKV.Value)) }) @@ -134,7 +134,7 @@ var _ = Describe("Kv mongodb service", func() { Describe("greedy find by kv and labels", func() { Context("with labels app ", func() { - kvs, err := s.Find("default", kv.WithKey("timeout"), kv.WithLabels(map[string]string{ + kvs, err := s.Find("default", dao.WithKey("timeout"), dao.WithLabels(map[string]string{ "app": "mall", })) It("should not return err", func() { @@ -148,9 +148,9 @@ var _ = Describe("Kv mongodb service", func() { }) Describe("exact find by kv and labels", func() { Context("with labels app ", func() { - kvs, err := s.Find("default", kv.WithKey("timeout"), kv.WithLabels(map[string]string{ + kvs, err := s.Find("default", dao.WithKey("timeout"), dao.WithLabels(map[string]string{ "app": "mall", - }), kv.WithExactLabels()) + }), dao.WithExactLabels()) It("should not return err", func() { Expect(err).Should(BeNil()) }) @@ -162,9 +162,9 @@ var _ = Describe("Kv mongodb service", func() { }) Describe("exact find by labels", func() { Context("with labels app ", func() { - kvs, err := s.Find("default", kv.WithLabels(map[string]string{ + kvs, err := s.Find("default", dao.WithLabels(map[string]string{ "app": "mall", - }), kv.WithExactLabels()) + }), dao.WithExactLabels()) It("should not return err", func() { Expect(err).Should(BeNil()) }) @@ -176,7 +176,7 @@ var _ = Describe("Kv mongodb service", func() { }) Describe("greedy find by labels", func() { Context("with labels app ans service ", func() { - kvs, err := s.Find("default", kv.WithLabels(map[string]string{ + kvs, err := s.Find("default", dao.WithLabels(map[string]string{ "app": "mall", "service": "cart", })) diff --git a/server/kv/model_suite_test.go b/server/dao/model_suite_test.go similarity index 98% copy from server/kv/model_suite_test.go copy to server/dao/model_suite_test.go index 965802e..a57389e 100644 --- a/server/kv/model_suite_test.go +++ b/server/dao/model_suite_test.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package kv_test +package dao_test import ( "testing" diff --git a/server/kv/mongodb.go b/server/dao/mongodb.go similarity index 98% rename from server/kv/mongodb.go rename to server/dao/mongodb.go index 37664ae..cbaabad 100644 --- a/server/kv/mongodb.go +++ b/server/dao/mongodb.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package kv +package dao import ( "context" @@ -141,8 +141,8 @@ func (s *MongodbService) Exist(key, domain string, labels model.Labels) (string, //Find 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 -func (s *MongodbService) Find(domain string, options ...CallOption) ([]*model.KV, error) { - opts := CallOptions{} +func (s *MongodbService) Find(domain string, options ...FindOption) ([]*model.KV, error) { + opts := FindOptions{} for _, o := range options { o(&opts) } @@ -271,7 +271,7 @@ func (s *MongodbService) getLatest(id primitive.ObjectID) (*model.KVHistory, err } return h, nil } -func NewMongoService(opts Options) (Service, error) { +func NewMongoService(opts Options) (KV, error) { if opts.Timeout == 0 { opts.Timeout = DefaultTimeout } diff --git a/server/kv/options.go b/server/dao/options.go similarity index 79% rename from server/kv/options.go rename to server/dao/options.go index aabe1ec..2116b67 100644 --- a/server/kv/options.go +++ b/server/dao/options.go @@ -15,35 +15,35 @@ * limitations under the License. */ -package kv +package dao import "github.com/apache/servicecomb-kie/pkg/model" -type CallOptions struct { +type FindOptions struct { ExactLabels bool Key string Labels model.Labels } -type CallOption func(*CallOptions) +type FindOption func(*FindOptions) //WithExactLabels tell model service to return only one kv matches the labels -func WithExactLabels() CallOption { - return func(o *CallOptions) { +func WithExactLabels() FindOption { + return func(o *FindOptions) { o.ExactLabels = true } } //WithKey find by key -func WithKey(key string) CallOption { - return func(o *CallOptions) { +func WithKey(key string) FindOption { + return func(o *FindOptions) { o.Key = key } } //WithLabels find kv by labels -func WithLabels(labels model.Labels) CallOption { - return func(o *CallOptions) { +func WithLabels(labels model.Labels) FindOption { + return func(o *FindOptions) { o.Labels = labels } } diff --git a/server/kv/model_suite_test.go b/server/handler/noop_auth_handler.go similarity index 54% rename from server/kv/model_suite_test.go rename to server/handler/noop_auth_handler.go index 965802e..b2c4a20 100644 --- a/server/kv/model_suite_test.go +++ b/server/handler/noop_auth_handler.go @@ -15,30 +15,29 @@ * limitations under the License. */ -package kv_test +package handler import ( - "testing" - - "github.com/go-chassis/paas-lager" - "github.com/go-mesh/openlogging" - . "github.com/onsi/ginkgo" - "github.com/onsi/ginkgo/reporters" - . "github.com/onsi/gomega" + "github.com/go-chassis/go-chassis/core/handler" + "github.com/go-chassis/go-chassis/core/invocation" ) -func TestModel(t *testing.T) { - RegisterFailHandler(Fail) - junitReporter := reporters.NewJUnitReporter("junit.xml") - RunSpecsWithDefaultAndCustomReporters(t, "Model Suite", []Reporter{junitReporter}) +//NoopAuthHandler not need implement any logic +//developer can extend authenticate and authorization by set new handler in chassis.yaml +type NoopAuthHandler struct{} + +func (bk *NoopAuthHandler) Handle(chain *handler.Chain, inv *invocation.Invocation, cb invocation.ResponseCallBack) { + inv.SetMetadata("domain", "default") + chain.Next(inv, cb) } -var _ = BeforeSuite(func() { - log.Init(log.Config{ - Writers: []string{"stdout"}, - LoggerLevel: "DEBUG", - }) +func newDomainResolver() handler.Handler { + return &NoopAuthHandler{} +} - logger := log.NewLogger("ut") - openlogging.SetLogger(logger) -}) +func (bk *NoopAuthHandler) Name() string { + return "auth-handler" +} +func init() { + handler.RegisterHandler("auth-handler", newDomainResolver) +} diff --git a/server/resource/v1/common.go b/server/resource/v1/common.go new file mode 100644 index 0000000..5d7bceb --- /dev/null +++ b/server/resource/v1/common.go @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package v1 + +import ( + "encoding/json" + "fmt" + "github.com/apache/servicecomb-kie/pkg/model" + "github.com/go-chassis/go-chassis/server/restful" + "github.com/go-mesh/openlogging" +) + +const ( + FindExact = "exact" + FindMany = "greedy" + MsgDomainMustNotBeEmpty = "domain must not be empty" + MsgIllegalFindPolicy = "value of header X-Find can be greedy or exact" + MsgIllegalLabels = "label's value can not be empty, " + + "label can not be duplicated, please check your query parameters" +) + +func ReadDomain(context *restful.Context) interface{} { + return context.ReadRestfulRequest().Attribute("domain") +} +func ReadFindPolicy(context *restful.Context) string { + policy := context.ReadRestfulRequest().HeaderParameter("X-Find") + if policy == "" { + return FindMany + } + return policy +} +func WriteErrResponse(context *restful.Context, status int, msg string) { + context.WriteHeader(status) + b, _ := json.MarshalIndent(&ErrorMsg{Msg: msg}, "", " ") + context.Write(b) +} + +func ErrLog(action string, kv *model.KV, err error) { + openlogging.Error(fmt.Sprintf("[%s] [%v] err:%s", action, kv, err.Error())) +} + +func InfoLog(action string, kv *model.KV) { + openlogging.Info( + fmt.Sprintf("[%s] [%s:%s] in [%s] success", action, kv.Key, kv.Value, kv.Domain)) +} diff --git a/server/kv/errors.go b/server/resource/v1/doc_struct.go similarity index 61% rename from server/kv/errors.go rename to server/resource/v1/doc_struct.go index 958a015..a0402ae 100644 --- a/server/kv/errors.go +++ b/server/resource/v1/doc_struct.go @@ -15,21 +15,14 @@ * limitations under the License. */ -package kv +package v1 -import ( - "errors" - "fmt" - - "github.com/apache/servicecomb-kie/pkg/model" - "github.com/go-mesh/openlogging" -) - -//ErrAction will wrap raw error to biz error and return -//it record audit log for mongodb operation failure like find, insert, update, deletion -func ErrAction(action, key string, labels model.Labels, domain string, err error) error { - msg := fmt.Sprintf("can not [%s] [%s] in [%s] with [%s],err: %s", action, key, domain, labels, err.Error()) - openlogging.Error(msg) - return errors.New(msg) +type KVBody struct { + Labels map[string]string `json:"labels"` + ValueType string `json:"valueType"` + Value string `json:"value"` +} +type ErrorMsg struct { + Msg string `json:"msg"` } diff --git a/server/resource/v1/kv_resource.go b/server/resource/v1/kv_resource.go new file mode 100644 index 0000000..7a9d66d --- /dev/null +++ b/server/resource/v1/kv_resource.go @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//v1 package hold http rest v1 API +package v1 + +import ( + "encoding/json" + "github.com/apache/servicecomb-kie/pkg/model" + "github.com/apache/servicecomb-kie/server/dao" + goRestful "github.com/emicklei/go-restful" + "github.com/go-chassis/go-chassis/server/restful" + "github.com/go-mesh/openlogging" + "net/http" +) + +type KVResource struct { +} + +func (r *KVResource) Put(context *restful.Context) { + var err error + key := context.ReadPathParameter("key") + kv := new(model.KV) + decoder := json.NewDecoder(context.ReadRequest().Body) + if err = decoder.Decode(kv); err != nil { + WriteErrResponse(context, http.StatusInternalServerError, err.Error()) + return + } + domain := ReadDomain(context) + if domain == nil { + WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty) + } + kv.Key = key + kv.Domain = domain.(string) + s, err := dao.NewKVService() + if err != nil { + WriteErrResponse(context, http.StatusInternalServerError, err.Error()) + return + } + kv, err = s.CreateOrUpdate(kv) + if err != nil { + ErrLog("put", kv, err) + WriteErrResponse(context, http.StatusInternalServerError, err.Error()) + return + } + InfoLog("put", kv) + context.WriteHeader(http.StatusOK) + context.WriteHeaderAndJSON(http.StatusOK, kv, goRestful.MIME_JSON) + +} +func (r *KVResource) Find(context *restful.Context) { + var err error + key := context.ReadPathParameter("key") + if key == "" { + WriteErrResponse(context, http.StatusForbidden, "key must not be empty") + return + } + 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] + } + s, err := dao.NewKVService() + if err != nil { + WriteErrResponse(context, http.StatusInternalServerError, err.Error()) + return + } + domain := ReadDomain(context) + if domain == nil { + WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty) + return + } + policy := ReadFindPolicy(context) + var kvs []*model.KV + switch policy { + case FindMany: + kvs, err = s.Find(domain.(string), dao.WithKey(key), dao.WithLabels(labels)) + case FindExact: + kvs, err = s.Find(domain.(string), dao.WithKey(key), dao.WithLabels(labels), + dao.WithExactLabels()) + default: + WriteErrResponse(context, http.StatusBadRequest, MsgIllegalFindPolicy) + return + } + if err != nil { + WriteErrResponse(context, http.StatusInternalServerError, err.Error()) + return + } + err = context.WriteHeaderAndJSON(http.StatusOK, kvs, goRestful.MIME_JSON) + if err != nil { + openlogging.Error(err.Error()) + } + +} +func (r *KVResource) FindByLabels(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] + } + s, err := dao.NewKVService() + if err != nil { + WriteErrResponse(context, http.StatusInternalServerError, err.Error()) + return + } + domain := ReadDomain(context) + if domain == nil { + WriteErrResponse(context, http.StatusInternalServerError, MsgDomainMustNotBeEmpty) + return + } + policy := ReadFindPolicy(context) + var kvs []*model.KV + switch policy { + case FindMany: + kvs, err = s.Find(domain.(string), dao.WithLabels(labels)) + case FindExact: + kvs, err = s.Find(domain.(string), dao.WithLabels(labels), + dao.WithExactLabels()) + default: + WriteErrResponse(context, http.StatusBadRequest, MsgIllegalFindPolicy) + return + } + err = context.WriteHeaderAndJSON(http.StatusOK, kvs, goRestful.MIME_JSON) + if err != nil { + openlogging.Error(err.Error()) + } + +} +func (r *KVResource) Delete(context *restful.Context) { + +} + +//URLPatterns defined config operations +func (r *KVResource) URLPatterns() []restful.Route { + return []restful.Route{ + { + Method: http.MethodPut, + Path: "/v1/kv/{key}", + ResourceFuncName: "Put", + FuncDesc: "create or update key value", + Parameters: []*restful.Parameters{ + { + DataType: "string", + Name: "key", + ParamType: goRestful.PathParameterKind, + }, { + DataType: "string", + Name: "X-Domain-Name", + ParamType: goRestful.HeaderParameterKind, + Desc: "set kv to other tenant", + }, { + DataType: "string", + Name: "X-Realm", + ParamType: goRestful.HeaderParameterKind, + Desc: "set kv to heterogeneous config server", + }, + }, + Returns: []*restful.Returns{ + { + Code: http.StatusOK, + Message: "true", + }, + }, + Consumes: []string{"application/json"}, + Produces: []string{"application/json"}, + Read: &KVBody{}, + }, { + Method: http.MethodGet, + Path: "/v1/kv/{key}", + ResourceFuncName: "Find", + FuncDesc: "get key values by key and labels", + Parameters: []*restful.Parameters{ + { + DataType: "string", + Name: "key", + ParamType: goRestful.PathParameterKind, + }, { + DataType: "string", + Name: "X-Domain-Name", + ParamType: goRestful.HeaderParameterKind, + }, { + DataType: "string", + Name: "X-Find", + ParamType: goRestful.HeaderParameterKind, + Desc: "greedy or exact", + }, + }, + Returns: []*restful.Returns{ + { + Code: http.StatusOK, + Message: "get key value success", + Model: []*KVBody{}, + }, + }, + Consumes: []string{"application/json"}, + Produces: []string{"application/json"}, + Read: &KVBody{}, + }, { + Method: http.MethodGet, + Path: "/v1/kv", + ResourceFuncName: "FindByLabels", + FuncDesc: "find key values only by labels", + Parameters: []*restful.Parameters{ + { + DataType: "string", + Name: "X-Domain-Name", + ParamType: goRestful.HeaderParameterKind, + }, { + DataType: "string", + Name: "X-Find", + ParamType: goRestful.HeaderParameterKind, + Desc: "greedy or exact", + }, + }, + Returns: []*restful.Returns{ + { + Code: http.StatusOK, + Message: "get key value success", + Model: []*KVBody{}, + }, + }, + Consumes: []string{"application/json"}, + Produces: []string{"application/json"}, + }, + } +}
