Skip to content

Commit

Permalink
baetyl property and cache
Browse files Browse the repository at this point in the history
  • Loading branch information
xumengdi01 authored Jul 29, 2020
1 parent 7c21e21 commit 5dec7b7
Show file tree
Hide file tree
Showing 23 changed files with 1,047 additions and 10 deletions.
13 changes: 12 additions & 1 deletion api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type API struct {
pkiService service.PKIService
initService service.InitializeService
authService service.AuthService
propertyService service.PropertyService
cacheService service.CacheService
}

// NewAPI NewAPI
Expand Down Expand Up @@ -86,7 +88,14 @@ func NewAPI(config *config.CloudConfig) (*API, error) {
if err != nil {
return nil, err
}

propertyService, err := service.NewPropertyService(config)
if err != nil {
return nil, err
}
cacheService, err := service.NewCacheService(config)
if err != nil {
return nil, err
}
return &API{
applicationService: applicationService,
nodeService: nodeService,
Expand All @@ -103,5 +112,7 @@ func NewAPI(config *config.CloudConfig) (*API, error) {
pkiService: pkiService,
initService: initService,
authService: authService,
propertyService: propertyService,
cacheService: cacheService,
}, nil
}
50 changes: 50 additions & 0 deletions api/property.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package api

import (
"github.com/baetyl/baetyl-cloud/common"
"github.com/baetyl/baetyl-cloud/models"
)

func (api *API) CreateProperty(c *common.Context) (interface{}, error) {
property := &models.Property{}
err := c.LoadBody(property)
if err != nil {
return nil, err
}
return nil, api.propertyService.CreateProperty(property)
}

func (api *API) DeleteProperty(c *common.Context) (interface{}, error) {
return nil, api.propertyService.DeleteProperty(c.Param("key"))
}

func (api *API) ListProperty(c *common.Context) (interface{}, error) {
params := &models.Filter{}
if err := c.Bind(params); err != nil {
return nil, err
}
params.Format()
properties, err := api.propertyService.ListProperty(params)
if err != nil {
return nil, err
}
count, err := api.propertyService.CountProperty(params.Name)
if err != nil {
return nil, err
}
return models.MisData{
Count: count,
Rows: properties,
}, nil
}

func (api *API) UpdateProperty(c *common.Context) (interface{}, error) {
property := &models.Property{
Key: c.Param("key"),
}
err := c.LoadBody(property)
if err != nil {
return nil, err
}
return nil, api.propertyService.UpdateProperty(property)
}
140 changes: 140 additions & 0 deletions api/property_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package api

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"

"github.com/baetyl/baetyl-cloud/common"
plugin "github.com/baetyl/baetyl-cloud/mock/service"
"github.com/baetyl/baetyl-cloud/models"
"github.com/gin-gonic/gin"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)

func initPropertyAPI(t *testing.T) (*API, *gin.Engine, *gomock.Controller) {
api := &API{}
router := gin.Default()
mockCtl := gomock.NewController(t)
mockIM := func(c *gin.Context) { common.NewContext(c).SetNamespace("default") }

v1 := router.Group("v1")
{
property := v1.Group("/properties")

property.GET("", mockIM, common.WrapperMis(api.ListProperty))
property.POST("", mockIM, common.WrapperMis(api.CreateProperty))
property.DELETE("/:key", mockIM, common.WrapperMis(api.DeleteProperty))
property.PUT("/:key", mockIM, common.WrapperMis(api.UpdateProperty))
}
return api, router, mockCtl
}

func genProperty() *models.Property {
return &models.Property{
Key: "bae",
Value: "http://test",
}
}

func TestCreateProperty(t *testing.T) {
api, router, ctl := initPropertyAPI(t)
rs := plugin.NewMockPropertyService(ctl)
api.propertyService = rs

property := genProperty()

rs.EXPECT().CreateProperty(property).Return(nil).Times(1)
// good case
body, _ := json.Marshal(property)
req, _ := http.NewRequest(http.MethodPost, "/v1/properties", bytes.NewReader(body))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
// empty body
req, _ = http.NewRequest(http.MethodPost, "/v1/properties", nil)
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)

}

func TestDeleteProperty(t *testing.T) {
api, router, ctl := initPropertyAPI(t)
rs := plugin.NewMockPropertyService(ctl)
api.propertyService = rs

property := genProperty()

rs.EXPECT().DeleteProperty(gomock.Any()).Return(nil).Times(1)

req, _ := http.NewRequest(http.MethodDelete, "/v1/properties/"+property.Key, nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}

func TestListProperty(t *testing.T) {
api, router, ctl := initPropertyAPI(t)
rs := plugin.NewMockPropertyService(ctl)
api.propertyService = rs

mConf := genProperty()
page := &models.Filter{
PageNo: 1,
PageSize: 2,
Name: "%",
}
// good case
rs.EXPECT().ListProperty(page).Return([]models.Property{*mConf}, nil).Times(1)
rs.EXPECT().CountProperty(page.Name).Return(1, nil).Times(1)

req, _ := http.NewRequest(http.MethodGet, "/v1/properties?pageNo=1&pageSize=2", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)

page = &models.Filter{
PageNo: 1,
PageSize: 20,
Name: "%",
}
// List error case
rs.EXPECT().ListProperty(page).Return([]models.Property{*mConf}, fmt.Errorf("GetResource error")).Times(1)
req, _ = http.NewRequest(http.MethodGet, "/v1/properties", nil)
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
// Count error case
rs.EXPECT().ListProperty(page).Return([]models.Property{*mConf}, nil).Times(1)
rs.EXPECT().CountProperty(page.Name).Return(0, fmt.Errorf("GetResource error")).Times(1)
req, _ = http.NewRequest(http.MethodGet, "/v1/properties", nil)
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}

func TestUpdateProperty(t *testing.T) {
api, router, ctl := initPropertyAPI(t)
rs := plugin.NewMockPropertyService(ctl)
api.propertyService = rs

property := genProperty()
rs.EXPECT().UpdateProperty(property).Return(nil).Times(1)
// good case
body, _ := json.Marshal(property)
req, _ := http.NewRequest(http.MethodPut, "/v1/properties/"+property.Key, bytes.NewReader(body))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
// empty body
req, _ = http.NewRequest(http.MethodPut, "/v1/properties/"+property.Key, nil)
w = httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)

}
47 changes: 46 additions & 1 deletion common/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/baetyl/baetyl-go/v2/log"
"github.com/baetyl/baetyl-go/v2/utils"
"github.com/gin-gonic/gin"
"github.com/satori/go.uuid"
uuid "github.com/satori/go.uuid"
"gopkg.in/go-playground/validator.v9"
)

Expand Down Expand Up @@ -211,3 +211,48 @@ func _toJsonString(obj interface{}) string {
data, _ := json.Marshal(obj)
return string(data)
}

func WrapperMis(handler HandlerFunc) func(c *gin.Context) {
return func(c *gin.Context) {
cc := NewContext(c)
defer func() {
if r := recover(); r != nil {
err, ok := r.(error)
if !ok {
err = Error(ErrUnknown, Field("error", r))
}
log.L().Info("handle a panic", log.Any(cc.GetTrace()), log.Code(err), log.Error(err), log.Any("panic", string(debug.Stack())))
PopulateFailedMisResponse(cc, err, false)
}
}()
res, err := handler(cc)
if err != nil {
log.L().Error("failed to handler request", log.Any(cc.GetTrace()), log.Code(err), log.Error(err))
PopulateFailedMisResponse(cc, err, false)
return
}
log.L().Debug("process success", log.Any(cc.GetTrace()), log.Any("response", _toJsonString(res)))
// unlike JSON, does not replace special html characters with their unicode entities. eg: JSON(&)->'\u0026' PureJSON(&)->'&'
cc.PureJSON(http.StatusOK, gin.H{
"status": 0,
"msg": "ok",
"data": res,
})
}
}

// PopulateFailedMisResponse PopulateFailedMisResponse
func PopulateFailedMisResponse(cc *Context, err error, abort bool) {
var status int = http.StatusOK
log.L().Error("process failed.", log.Any(cc.GetTrace()), log.Code(err))

body := gin.H{
"status": 1,
"msg": err.Error(),
}
if abort {
cc.AbortWithStatusJSON(status, body)
} else {
cc.JSON(status, body)
}
}
57 changes: 57 additions & 0 deletions common/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,60 @@ func (s *stringReaderCloser) Read(p []byte) (n int, err error) {
func (s *stringReaderCloser) Close() error {
return nil
}

func TestWrapperMis(t *testing.T) {
test200 := func(c *Context) (interface{}, error) {
return nil, nil
}

test404 := func(c *Context) (interface{}, error) {
return nil, Error(ErrResourceNotFound, Field("name", "test"))
}

test401 := func(c *Context) (interface{}, error) {
return nil, Error(ErrRequestAccessDenied)
}
test400 := func(c *Context) (interface{}, error) {
return nil, Error(ErrRequestParamInvalid)
}

testPanic := func(c *Context) (interface{}, error) {
panic("panic test")
}
router := gin.Default()
router.GET("/200", WrapperMis(test200))
router.GET("/404", WrapperMis(test404))
router.GET("/400", WrapperMis(test400))
router.GET("/401", WrapperMis(test401))
router.GET("/panic", WrapperMis(testPanic))

// 200
req, _ := http.NewRequest(http.MethodGet, "/200", nil)
w1 := httptest.NewRecorder()
router.ServeHTTP(w1, req)
assert.Equal(t, http.StatusOK, w1.Code)

// 400
req, _ = http.NewRequest(http.MethodGet, "/404", nil)
w2 := httptest.NewRecorder()
router.ServeHTTP(w2, req)
assert.Equal(t, http.StatusOK, w2.Code)

// 500
req, _ = http.NewRequest(http.MethodGet, "/panic", nil)
w3 := httptest.NewRecorder()
router.ServeHTTP(w3, req)
assert.Equal(t, http.StatusOK, w3.Code)

// 401
req, _ = http.NewRequest(http.MethodGet, "/401", nil)
w4 := httptest.NewRecorder()
router.ServeHTTP(w4, req)
assert.Equal(t, http.StatusOK, w4.Code)

// 400
req, _ = http.NewRequest(http.MethodGet, "/400", nil)
w5 := httptest.NewRecorder()
router.ServeHTTP(w5, req)
assert.Equal(t, http.StatusOK, w5.Code)
}
21 changes: 19 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,29 @@ const (
AdminServerPort = "ADMIN_PORT"
NodeServerPort = "NODE_PORT"
ActiveServerPort = "ACTIVE_PORT"
MisServerPort = "MIS_PORT"
)

// CloudConfig baetyl-cloud config
type CloudConfig struct {
ActiveServer Server `yaml:"activeServer" json:"activeServer" default:"{\"port\":\":9003\",\"readTimeout\":30000000000,\"writeTimeout\":30000000000,\"shutdownTime\":3000000000}"`
AdminServer Server `yaml:"adminServer" json:"adminServer" default:"{\"port\":\":9004\",\"readTimeout\":30000000000,\"writeTimeout\":30000000000,\"shutdownTime\":3000000000}"`
NodeServer NodeServer `yaml:"nodeServer" json:"nodeServer" default:"{\"port\":\":9005\",\"readTimeout\":30000000000,\"writeTimeout\":30000000000,\"shutdownTime\":3000000000,\"commonName\":\"common-name\"}"`
MisServer MisServer `yaml:"misServer" json:"misServer" default:"{\"port\":\":9006\",\"readTimeout\":30000000000,\"writeTimeout\":30000000000,\"shutdownTime\":3000000000,\"authToken\":\"baetyl-cloud-token\",\"tokenHeader\":\"baetyl-cloud-token\",\"userHeader\":\"baetyl-cloud-user\"}"`
LogInfo log.Config `yaml:"logger" json:"logger"`
Plugin struct {
Cache struct {
ExpirationDuration time.Duration `yaml:"expirationDuration" json:"expirationDuration" default:"10m"`
} `yaml:"cache" json:"cache"`
Plugin struct {
PKI string `yaml:"pki" json:"pki" default:"defaultpki"`
Auth string `yaml:"auth" json:"auth" default:"defaultauth"`
License string `yaml:"license" json:"license" default:"defaultlicense"`
Shadow string `yaml:"shadow" json:"shadow" default:"database"`
Objects []string `yaml:"objects" json:"objects" default:"[]"`
Functions []string `yaml:"functions" json:"functions" default:"[]"`

Property string `yaml:"property" json:"property" default:"database"`
// TODO: deprecated

ModelStorage string `yaml:"modelStorage" json:"modelStorage" default:"kubernetes"`
DatabaseStorage string `yaml:"databaseStorage" json:"databaseStorage" default:"database"`
} `yaml:"plugin" json:"plugin"`
Expand All @@ -39,6 +45,13 @@ type NodeServer struct {
CommonName string `yaml:"commonName" json:"commonName" default:"common-name"`
}

type MisServer struct {
Server `yaml:",inline" json:",inline"`
AuthToken string `yaml:"authToken" json:"authToken" default:"baetyl-cloud-token"`
TokenHeader string `yaml:"tokenHeader" json:"tokenHeader" default:"baetyl-cloud-token"`
UserHeader string `yaml:"userHeader" json:"userHeader" default:"baetyl-cloud-user"`
}

// Server server config
type Server struct {
Port string `yaml:"port" json:"port"`
Expand All @@ -61,4 +74,8 @@ func SetPortFromEnv(cfg *CloudConfig) {
if nodePort != "" {
cfg.NodeServer.Port = ":" + nodePort
}
misPort := os.Getenv(MisServerPort)
if misPort != "" {
cfg.MisServer.Port = ":" + misPort
}
}
Loading

0 comments on commit 5dec7b7

Please sign in to comment.