This is an automated email from the ASF dual-hosted git repository.
liujun pushed a commit to branch refactor-with-go
in repository https://gitbox.apache.org/repos/asf/dubbo-admin.git
The following commit(s) were added to refs/heads/refactor-with-go by this push:
new 5bc791aa feat: refactor service mock with go (#1060)
5bc791aa is described below
commit 5bc791aab97dff442807f2e3460388e96a3f4182
Author: WangGuan <[email protected]>
AuthorDate: Tue Mar 28 19:06:31 2023 +0800
feat: refactor service mock with go (#1060)
---
.gitignore | 3 +
conf/dubboadmin.yml | 3 +-
go.mod | 10 ++
go.sum | 20 +++
pkg/admin/config/config.go | 30 ++++
pkg/admin/handlers/mock_rule.go | 96 ++++++++++++
pkg/admin/mapper/mock_rule_mapper.go | 59 ++++++++
pkg/admin/mapper/mock_rule_mapper_mock.go | 125 ++++++++++++++++
pkg/admin/model/mock_rule.go | 64 ++++++++
pkg/admin/router/router.go | 7 +
pkg/admin/services/mock_rule_service.go | 33 +++++
pkg/admin/services/mock_rule_service_impl.go | 85 +++++++++++
pkg/admin/services/mock_rule_service_impl_test.go | 172 ++++++++++++++++++++++
13 files changed, 706 insertions(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index 0aae576a..9ba98974 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,9 @@ target/
*.iml
*.iws
+# vscode ignore
+.vscode/
+
# temp ignore
*.log*
*.cache
diff --git a/conf/dubboadmin.yml b/conf/dubboadmin.yml
index b8d0fdd7..8df11f61 100644
--- a/conf/dubboadmin.yml
+++ b/conf/dubboadmin.yml
@@ -18,4 +18,5 @@ admin:
address: zookeeper://127.0.0.1:2181
config-center: zookeeper://127.0.0.1:2181
metadata-report:
- address: zookeeper://127.0.0.1:2181
\ No newline at end of file
+ address: zookeeper://127.0.0.1:2181
+ # mysql-dsn:
root:password@tcp(127.0.0.1:3306)/dubbo-admin?charset=utf8&parseTime=true
\ No newline at end of file
diff --git a/go.mod b/go.mod
index a3d7d09f..02f12f3b 100644
--- a/go.mod
+++ b/go.mod
@@ -45,6 +45,13 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.2.3
)
+require (
+ github.com/go-sql-driver/mysql v1.7.0 // indirect
+ github.com/jinzhu/inflection v1.0.0 // indirect
+ github.com/jinzhu/now v1.1.5 // indirect
+ github.com/mattn/go-sqlite3 v1.14.16 // indirect
+)
+
require (
contrib.go.opencensus.io/exporter/prometheus v0.4.1 // indirect
github.com/RoaringBitmap/roaring v1.1.0 // indirect
@@ -144,6 +151,9 @@ require (
gopkg.in/ini.v1 v1.51.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+ gorm.io/driver/mysql v1.4.7
+ gorm.io/driver/sqlite v1.4.4
+ gorm.io/gorm v1.24.6
k8s.io/klog/v2 v2.80.1 // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
diff --git a/go.sum b/go.sum
index e3273fbe..86c96788 100644
--- a/go.sum
+++ b/go.sum
@@ -261,6 +261,9 @@ github.com/go-playground/validator/v10 v10.11.1
h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJ
github.com/go-playground/validator/v10 v10.11.1/go.mod
h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-resty/resty/v2 v2.7.0/go.mod
h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-sql-driver/mysql v1.4.0/go.mod
h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.7.0
h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
+github.com/go-sql-driver/mysql v1.7.0/go.mod
h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-stack/stack v1.8.0
h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod
h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod
h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.0.2/go.mod
h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
@@ -452,6 +455,11 @@ github.com/jessevdk/go-flags v1.4.0/go.mod
h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
github.com/jhump/protoreflect v1.6.0/go.mod
h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
github.com/jinzhu/copier v0.3.5/go.mod
h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
+github.com/jinzhu/inflection v1.0.0
h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod
h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.4/go.mod
h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod
h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod
h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0
h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod
h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -535,6 +543,10 @@ github.com/mattn/go-isatty v0.0.14/go.mod
h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16
h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod
h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.2/go.mod
h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-sqlite3 v1.14.15/go.mod
h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/mattn/go-sqlite3 v1.14.16
h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
+github.com/mattn/go-sqlite3 v1.14.16/go.mod
h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/matttproud/golang_protobuf_extensions v1.0.1
h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod
h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2
h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM=
github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod
h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
@@ -1280,6 +1292,14 @@ gopkg.in/yaml.v3
v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod
h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y=
+gorm.io/driver/mysql v1.4.7/go.mod
h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc=
+gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
+gorm.io/driver/sqlite v1.4.4/go.mod
h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
+gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
+gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
+gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s=
+gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gotest.tools v2.2.0+incompatible/go.mod
h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod
h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/pkg/admin/config/config.go b/pkg/admin/config/config.go
index 72f1c34e..def8c4a8 100644
--- a/pkg/admin/config/config.go
+++ b/pkg/admin/config/config.go
@@ -29,9 +29,13 @@ import (
"dubbo.apache.org/dubbo-go/v3/config_center"
"dubbo.apache.org/dubbo-go/v3/metadata/report"
"dubbo.apache.org/dubbo-go/v3/registry"
+ "gorm.io/driver/mysql"
+ "gorm.io/driver/sqlite"
+ "gorm.io/gorm"
"github.com/apache/dubbo-admin/pkg/admin/constant"
_ "github.com/apache/dubbo-admin/pkg/admin/imports"
+ "github.com/apache/dubbo-admin/pkg/admin/model"
"gopkg.in/yaml.v2"
)
@@ -51,6 +55,7 @@ type Admin struct {
ConfigCenter string `yaml:"config-center"`
MetadataReport AddressConfig `yaml:"metadata-report"`
Registry AddressConfig `yaml:"registry"`
+ MysqlDsn string `yaml:"mysql-dsn"`
}
var (
@@ -58,6 +63,8 @@ var (
RegistryCenter registry.Registry
MetadataReportCenter report.MetadataReport
+ DataBase *gorm.DB // for service mock
+
Group string
)
@@ -77,9 +84,13 @@ func LoadConfig() {
}
var config Config
yaml.Unmarshal(content, &config)
+
address := config.Admin.ConfigCenter
registryAddress := config.Admin.Registry.Address
metadataReportAddress := config.Admin.MetadataReport.Address
+
+ loadDatabaseConfig(config.Admin.MysqlDsn)
+
PrometheusIp = config.Prometheus.Ip
PrometheusPort = config.Prometheus.Port
if PrometheusIp == "" {
@@ -88,6 +99,7 @@ func LoadConfig() {
if PrometheusPort == "" {
PrometheusPort = "9090"
}
+
if len(address) > 0 {
c := newAddressConfig(address)
factory, err :=
extension.GetConfigCenterFactory(c.getProtocol())
@@ -170,3 +182,21 @@ func newAddressConfig(address string) AddressConfig {
}
return config
}
+
+// load database for mock rule storage, if dsn is empty, use sqlite in memory
+func loadDatabaseConfig(dsn string) {
+ var db *gorm.DB
+ var err error
+ if dsn == "" {
+ db, err = gorm.Open(sqlite.Open("file::memory:?cache=shared"),
&gorm.Config{})
+ } else {
+ db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
+ }
+ if err != nil {
+ panic(err)
+ } else {
+ DataBase = db
+ // init table
+ DataBase.AutoMigrate(&model.MockRuleEntity{})
+ }
+}
diff --git a/pkg/admin/handlers/mock_rule.go b/pkg/admin/handlers/mock_rule.go
new file mode 100644
index 00000000..27ad3a35
--- /dev/null
+++ b/pkg/admin/handlers/mock_rule.go
@@ -0,0 +1,96 @@
+/*
+ * 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 handlers
+
+import (
+ "net/http"
+ "strconv"
+
+ "github.com/apache/dubbo-admin/pkg/admin/mapper"
+ "github.com/apache/dubbo-admin/pkg/admin/model"
+ "github.com/apache/dubbo-admin/pkg/admin/services"
+ "github.com/apache/dubbo-admin/pkg/logger"
+ "github.com/gin-gonic/gin"
+)
+
+var mockRuleService services.MockRuleService = &services.MockRuleServiceImpl{
+ MockRuleMapper: &mapper.MockRuleMapperImpl{},
+ Logger: logger.Logger(),
+}
+
+func CreateOrUpdateMockRule(c *gin.Context) {
+ var mockRule *model.MockRule
+ if err := c.ShouldBindJSON(&mockRule); err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
+ if err := mockRuleService.CreateOrUpdateMockRule(mockRule); err != nil {
+ c.JSON(http.StatusInternalServerError, gin.H{"error":
err.Error()})
+ return
+ }
+
+ c.JSON(http.StatusCreated, true)
+}
+
+func DeleteMockRuleById(c *gin.Context) {
+ // TODO use c.Param("id") instead of http body
+ // id, err := strconv.ParseInt(c.Param("id"), 10, 64)
+
+ var mockRule *model.MockRule
+ if err := c.ShouldBindJSON(&mockRule); err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+ if mockRule.ID == 0 {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "id is required"})
+ return
+ }
+ if err := mockRuleService.DeleteMockRuleById(int64(mockRule.ID)); err
!= nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+ c.JSON(http.StatusOK, true)
+}
+
+func ListMockRulesByPage(c *gin.Context) {
+ filter := c.Query("filter")
+ offset, err := strconv.Atoi(c.DefaultQuery("offset", "0"))
+ if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
+ limit, err := strconv.Atoi(c.DefaultQuery("limit", "-1"))
+ if err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
+ mockRules, total, err := mockRuleService.ListMockRulesByPage(filter,
offset, limit)
+ if err != nil {
+ c.JSON(http.StatusInternalServerError, gin.H{"error":
err.Error()})
+ return
+ }
+
+ // FIXME: the response data is not compatible with the frontend
+ c.JSON(http.StatusOK, gin.H{
+ "total": total,
+ "content": mockRules,
+ })
+}
diff --git a/pkg/admin/mapper/mock_rule_mapper.go
b/pkg/admin/mapper/mock_rule_mapper.go
new file mode 100644
index 00000000..5123c547
--- /dev/null
+++ b/pkg/admin/mapper/mock_rule_mapper.go
@@ -0,0 +1,59 @@
+/*
+ * 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 mapper
+
+import (
+ "github.com/apache/dubbo-admin/pkg/admin/config"
+ "github.com/apache/dubbo-admin/pkg/admin/model"
+)
+
+type MockRuleMapper interface {
+ Create(mockRule *model.MockRuleEntity) error
+ Update(mockRule *model.MockRuleEntity) error
+ DeleteById(id int64) error
+ FindByServiceNameAndMethodName(serviceName, methodName string)
(*model.MockRuleEntity, error)
+ FindByPage(filter string, offset, limit int) ([]*model.MockRuleEntity,
int64, error)
+}
+
+type MockRuleMapperImpl struct{}
+
+func (m *MockRuleMapperImpl) Create(mockRule *model.MockRuleEntity) error {
+ return config.DataBase.Create(mockRule).Error
+}
+
+func (m *MockRuleMapperImpl) Update(mockRule *model.MockRuleEntity) error {
+ return config.DataBase.Updates(mockRule).Error
+}
+
+func (m *MockRuleMapperImpl) DeleteById(id int64) error {
+ return config.DataBase.Delete(&model.MockRuleEntity{}, id).Error
+}
+
+func (m *MockRuleMapperImpl) FindByServiceNameAndMethodName(serviceName,
methodName string) (*model.MockRuleEntity, error) {
+ var mockRule model.MockRuleEntity
+ err := config.DataBase.Where("service_name = ? and method_name = ?",
serviceName, methodName).Limit(1).Find(&mockRule).Error
+ return &mockRule, err
+}
+
+func (m *MockRuleMapperImpl) FindByPage(filter string, offset, limit int)
([]*model.MockRuleEntity, int64, error) {
+ var mockRules []*model.MockRuleEntity
+ var total int64
+ err := config.DataBase.Where("service_name like ?",
"%"+filter+"%").Offset(offset).Limit(limit).Find(&mockRules).Error
+ config.DataBase.Model(&model.MockRuleEntity{}).Where("service_name like
?", "%"+filter+"%").Count(&total)
+ return mockRules, total, err
+}
diff --git a/pkg/admin/mapper/mock_rule_mapper_mock.go
b/pkg/admin/mapper/mock_rule_mapper_mock.go
new file mode 100644
index 00000000..949133c8
--- /dev/null
+++ b/pkg/admin/mapper/mock_rule_mapper_mock.go
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+// Code generated by MockGen. DO NOT EDIT.
+// Source: github.com/apache/dubbo-admin/pkg/admin/mapper (interfaces:
MockRuleMapper)
+
+// Package mapper is a generated GoMock package.
+package mapper
+
+import (
+ reflect "reflect"
+
+ model "github.com/apache/dubbo-admin/pkg/admin/model"
+ gomock "github.com/golang/mock/gomock"
+)
+
+// MockMockRuleMapper is a mock of MockRuleMapper interface.
+type MockMockRuleMapper struct {
+ ctrl *gomock.Controller
+ recorder *MockMockRuleMapperMockRecorder
+}
+
+// MockMockRuleMapperMockRecorder is the mock recorder for MockMockRuleMapper.
+type MockMockRuleMapperMockRecorder struct {
+ mock *MockMockRuleMapper
+}
+
+// NewMockMockRuleMapper creates a new mock instance.
+func NewMockMockRuleMapper(ctrl *gomock.Controller) *MockMockRuleMapper {
+ mock := &MockMockRuleMapper{ctrl: ctrl}
+ mock.recorder = &MockMockRuleMapperMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockMockRuleMapper) EXPECT() *MockMockRuleMapperMockRecorder {
+ return m.recorder
+}
+
+// Create mocks base method.
+func (m *MockMockRuleMapper) Create(arg0 *model.MockRuleEntity) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Create", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Create indicates an expected call of Create.
+func (mr *MockMockRuleMapperMockRecorder) Create(arg0 interface{})
*gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create",
reflect.TypeOf((*MockMockRuleMapper)(nil).Create), arg0)
+}
+
+// DeleteById mocks base method.
+func (m *MockMockRuleMapper) DeleteById(arg0 int64) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "DeleteById", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// DeleteById indicates an expected call of DeleteById.
+func (mr *MockMockRuleMapperMockRecorder) DeleteById(arg0 interface{})
*gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteById",
reflect.TypeOf((*MockMockRuleMapper)(nil).DeleteById), arg0)
+}
+
+// FindByPage mocks base method.
+func (m *MockMockRuleMapper) FindByPage(arg0 string, arg1, arg2 int)
([]*model.MockRuleEntity, int64, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FindByPage", arg0, arg1, arg2)
+ ret0, _ := ret[0].([]*model.MockRuleEntity)
+ ret1, _ := ret[1].(int64)
+ ret2, _ := ret[2].(error)
+ return ret0, ret1, ret2
+}
+
+// FindByPage indicates an expected call of FindByPage.
+func (mr *MockMockRuleMapperMockRecorder) FindByPage(arg0, arg1, arg2
interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindByPage",
reflect.TypeOf((*MockMockRuleMapper)(nil).FindByPage), arg0, arg1, arg2)
+}
+
+// FindByServiceNameAndMethodName mocks base method.
+func (m *MockMockRuleMapper) FindByServiceNameAndMethodName(arg0, arg1 string)
(*model.MockRuleEntity, error) {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "FindByServiceNameAndMethodName", arg0, arg1)
+ ret0, _ := ret[0].(*model.MockRuleEntity)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// FindByServiceNameAndMethodName indicates an expected call of
FindByServiceNameAndMethodName.
+func (mr *MockMockRuleMapperMockRecorder) FindByServiceNameAndMethodName(arg0,
arg1 interface{}) *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock,
"FindByServiceNameAndMethodName",
reflect.TypeOf((*MockMockRuleMapper)(nil).FindByServiceNameAndMethodName),
arg0, arg1)
+}
+
+// Update mocks base method.
+func (m *MockMockRuleMapper) Update(arg0 *model.MockRuleEntity) error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Update", arg0)
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Update indicates an expected call of Update.
+func (mr *MockMockRuleMapperMockRecorder) Update(arg0 interface{})
*gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update",
reflect.TypeOf((*MockMockRuleMapper)(nil).Update), arg0)
+}
diff --git a/pkg/admin/model/mock_rule.go b/pkg/admin/model/mock_rule.go
new file mode 100644
index 00000000..eaab05db
--- /dev/null
+++ b/pkg/admin/model/mock_rule.go
@@ -0,0 +1,64 @@
+/*
+ * 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 model
+
+import (
+ "gorm.io/gorm"
+)
+
+type MockRule struct {
+ ID uint `json:"id"`
+ ServiceName string `json:"serviceName"`
+ MethodName string `json:"methodName"`
+ Rule string `json:"rule"`
+ Enable bool `json:"enable"`
+}
+
+func (m *MockRule) ToMockRuleEntity() *MockRuleEntity {
+ return &MockRuleEntity{
+ Model: gorm.Model{
+ ID: m.ID,
+ },
+ ServiceName: m.ServiceName,
+ MethodName: m.MethodName,
+ Rule: m.Rule,
+ Enable: m.Enable,
+ }
+}
+
+type MockRuleEntity struct {
+ gorm.Model
+ ServiceName string `gorm:"type:varchar(255)"`
+ MethodName string `gorm:"type:varchar(255)"`
+ Rule string `gorm:"type:text"`
+ Enable bool
+}
+
+func (m *MockRuleEntity) ToMockRule() *MockRule {
+ return &MockRule{
+ ID: uint(m.ID),
+ ServiceName: m.ServiceName,
+ MethodName: m.MethodName,
+ Rule: m.Rule,
+ Enable: m.Enable,
+ }
+}
+
+func (m *MockRuleEntity) TableName() string {
+ return "mock_rule"
+}
diff --git a/pkg/admin/router/router.go b/pkg/admin/router/router.go
index ebe157c4..b24ead20 100644
--- a/pkg/admin/router/router.go
+++ b/pkg/admin/router/router.go
@@ -67,5 +67,12 @@ func InitRouter() *gin.Engine {
conditionRoute.PUT("/disable/:id",
handlers.DisableConditionRoute)
}
+ mockRoute := router.Group("/api/:env/mock/rule")
+ {
+ mockRoute.POST("/", handlers.CreateOrUpdateMockRule)
+ mockRoute.DELETE("/", handlers.DeleteMockRuleById)
+ mockRoute.GET("/list", handlers.ListMockRulesByPage)
+ }
+
return router
}
diff --git a/pkg/admin/services/mock_rule_service.go
b/pkg/admin/services/mock_rule_service.go
new file mode 100644
index 00000000..3ae5ce4b
--- /dev/null
+++ b/pkg/admin/services/mock_rule_service.go
@@ -0,0 +1,33 @@
+/*
+ * 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 services
+
+import "github.com/apache/dubbo-admin/pkg/admin/model"
+
+type MockRuleService interface {
+ // create or update mock rule. if the request contains id, then will be
an update operation.
+ CreateOrUpdateMockRule(mockRule *model.MockRule) error
+
+ // delete the mock rule data by mock rule id.
+ DeleteMockRuleById(id int64) error
+
+ // list the mock rules by filter and return data by page.
+ ListMockRulesByPage(filter string, offset, limit int)
([]*model.MockRule, int64, error)
+
+ // TODO getMockData method, which depends on the implementation
corresponding to mock of dubbo-go.
+}
diff --git a/pkg/admin/services/mock_rule_service_impl.go
b/pkg/admin/services/mock_rule_service_impl.go
new file mode 100644
index 00000000..f6889961
--- /dev/null
+++ b/pkg/admin/services/mock_rule_service_impl.go
@@ -0,0 +1,85 @@
+/*
+ * 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 services
+
+import (
+ "errors"
+
+ "github.com/apache/dubbo-admin/pkg/admin/mapper"
+ "github.com/apache/dubbo-admin/pkg/admin/model"
+ "go.uber.org/zap"
+)
+
+type MockRuleServiceImpl struct {
+ MockRuleMapper mapper.MockRuleMapper
+ Logger *zap.Logger
+}
+
+func (m *MockRuleServiceImpl) CreateOrUpdateMockRule(mockRule *model.MockRule)
error {
+ if mockRule.ServiceName == "" || mockRule.MethodName == "" ||
mockRule.Rule == "" {
+ return nil
+ }
+
+ existRule, err :=
m.MockRuleMapper.FindByServiceNameAndMethodName(mockRule.ServiceName,
mockRule.MethodName)
+ if err != nil {
+ m.Logger.Error(err.Error())
+ return err
+ }
+
+ isExist := existRule.ID != 0
+ // check if we can save or update the rule, we need keep the
serviceName + methodName is unique.
+ if isExist {
+ if mockRule.ID != existRule.ID {
+ err := errors.New("service name and method name must be
unique")
+ m.Logger.Error(err.Error())
+ return err
+ }
+ if err := m.MockRuleMapper.Update(mockRule.ToMockRuleEntity());
err != nil {
+ m.Logger.Error(err.Error())
+ return err
+ }
+ } else {
+ if err := m.MockRuleMapper.Create(mockRule.ToMockRuleEntity());
err != nil {
+ m.Logger.Error(err.Error())
+ return err
+ }
+ }
+ return nil
+}
+
+func (m *MockRuleServiceImpl) DeleteMockRuleById(id int64) error {
+ if err := m.MockRuleMapper.DeleteById(id); err != nil {
+ m.Logger.Error(err.Error())
+ return err
+ }
+ return nil
+}
+
+func (m *MockRuleServiceImpl) ListMockRulesByPage(filter string, offset, limit
int) ([]*model.MockRule, int64, error) {
+ result, total, err := m.MockRuleMapper.FindByPage(filter, offset, limit)
+ if err != nil {
+ m.Logger.Error(err.Error())
+ return nil, 0, err
+ }
+
+ morkRules := make([]*model.MockRule, 0)
+ for _, mockRuleEntity := range result {
+ morkRules = append(morkRules, mockRuleEntity.ToMockRule())
+ }
+ return morkRules, total, nil
+}
diff --git a/pkg/admin/services/mock_rule_service_impl_test.go
b/pkg/admin/services/mock_rule_service_impl_test.go
new file mode 100644
index 00000000..b98be6ad
--- /dev/null
+++ b/pkg/admin/services/mock_rule_service_impl_test.go
@@ -0,0 +1,172 @@
+/*
+ * 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 services
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/apache/dubbo-admin/pkg/admin/mapper"
+ "github.com/apache/dubbo-admin/pkg/admin/model"
+ "github.com/apache/dubbo-admin/pkg/logger"
+ "github.com/golang/mock/gomock"
+)
+
+func TestMockRuleServiceImpl_CreateOrUpdateMockRule(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ mockMockRuleMapper := mapper.NewMockMockRuleMapper(ctrl)
+
+ mockUnExistData := &model.MockRule{}
+ createData := &model.MockRule{ID: 1, ServiceName: "testService1",
MethodName: "testMethod1", Rule: "exampleRule", Enable: true}
+ mockExistData := &model.MockRule{ID: 1, ServiceName: "testService2",
MethodName: "testMethod2", Rule: "exampleRule", Enable: true}
+ updateData := &model.MockRule{ID: 1, ServiceName: "testService2",
MethodName: "testMethod2", Rule: "exampleRuleAfterUpdate", Enable: true}
+
+
mockMockRuleMapper.EXPECT().FindByServiceNameAndMethodName(createData.ServiceName,
createData.MethodName).Return(mockUnExistData.ToMockRuleEntity(), nil)
+
mockMockRuleMapper.EXPECT().Create(createData.ToMockRuleEntity()).Return(nil)
+
mockMockRuleMapper.EXPECT().FindByServiceNameAndMethodName(mockExistData.ServiceName,
mockExistData.MethodName).Return(mockExistData.ToMockRuleEntity(), nil)
+
mockMockRuleMapper.EXPECT().Update(updateData.ToMockRuleEntity()).Return(nil)
+
+ type args struct {
+ mockRule *model.MockRule
+ }
+ tests := []struct {
+ name string
+ m *MockRuleServiceImpl
+ args args
+ wantErr bool
+ }{
+ {
+ name: "test create mock rule",
+ m: &MockRuleServiceImpl{
+ MockRuleMapper: mockMockRuleMapper,
+ Logger: logger.Logger(),
+ },
+ args: args{
+ mockRule: createData,
+ },
+ wantErr: false,
+ },
+ {
+ name: "test update mock rule",
+ m: &MockRuleServiceImpl{
+ MockRuleMapper: mockMockRuleMapper,
+ Logger: logger.Logger(),
+ },
+ args: args{
+ mockRule: updateData,
+ },
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err :=
tt.m.CreateOrUpdateMockRule(tt.args.mockRule); (err != nil) != tt.wantErr {
+
t.Errorf("MockRuleServiceImpl.CreateOrUpdateMockRule() error = %v, wantErr %v",
err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestMockRuleServiceImpl_DeleteMockRuleById(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ mockMockRuleMapper := mapper.NewMockMockRuleMapper(ctrl)
+
+ mockMockRuleMapper.EXPECT().DeleteById(int64(1)).Return(nil)
+
+ type args struct {
+ id int64
+ }
+ tests := []struct {
+ name string
+ m *MockRuleServiceImpl
+ args args
+ wantErr bool
+ }{
+ {
+ name: "test delete mock rule",
+ m: &MockRuleServiceImpl{
+ MockRuleMapper: mockMockRuleMapper,
+ Logger: logger.Logger(),
+ },
+ args: args{
+ id: 1,
+ },
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if err := tt.m.DeleteMockRuleById(tt.args.id); (err !=
nil) != tt.wantErr {
+
t.Errorf("MockRuleServiceImpl.DeleteMockRuleById() error = %v, wantErr %v",
err, tt.wantErr)
+ }
+ })
+ }
+}
+
+func TestMockRuleServiceImpl_ListMockRulesByPage(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ mockMockRuleMapper := mapper.NewMockMockRuleMapper(ctrl)
+
+ findResult := &model.MockRule{ID: 1, ServiceName: "testService2",
MethodName: "testMethod2", Rule: "exampleRule", Enable: true}
+ mockMockRuleMapper.EXPECT().FindByPage("", 0,
-1).Return([]*model.MockRuleEntity{findResult.ToMockRuleEntity()}, int64(1),
nil)
+
+ type args struct {
+ filter string
+ offset int
+ limit int
+ }
+ tests := []struct {
+ name string
+ m *MockRuleServiceImpl
+ args args
+ want []*model.MockRule
+ want1 int64
+ wantErr bool
+ }{
+ {
+ name: "test list mock rule",
+ m: &MockRuleServiceImpl{
+ MockRuleMapper: mockMockRuleMapper,
+ Logger: logger.Logger(),
+ },
+ args: args{
+ filter: "",
+ offset: 0,
+ limit: -1,
+ },
+ want: []*model.MockRule{findResult},
+ want1: 1,
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, got1, err :=
tt.m.ListMockRulesByPage(tt.args.filter, tt.args.offset, tt.args.limit)
+ if (err != nil) != tt.wantErr {
+
t.Errorf("MockRuleServiceImpl.ListMockRulesByPage() error = %v, wantErr %v",
err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+
t.Errorf("MockRuleServiceImpl.ListMockRulesByPage() got = %v, want %v", got,
tt.want)
+ }
+ if got1 != tt.want1 {
+
t.Errorf("MockRuleServiceImpl.ListMockRulesByPage() got1 = %v, want %v", got1,
tt.want1)
+ }
+ })
+ }
+}