diff --git a/devcloud/etc/application.toml b/devcloud/etc/application.toml index 8b94ce8..f32d246 100644 --- a/devcloud/etc/application.toml +++ b/devcloud/etc/application.toml @@ -11,7 +11,7 @@ database = "devcloud_go18" username = "root" password = "123456" - auto_migrate = true + auto_migrate = false debug = true [mongo] diff --git a/devcloud/mcenter/apps/policy/model.go b/devcloud/mcenter/apps/policy/model.go index 0b8ebf9..460f46e 100644 --- a/devcloud/mcenter/apps/policy/model.go +++ b/devcloud/mcenter/apps/policy/model.go @@ -1,18 +1,17 @@ package policy import ( - "encoding/json" - "fmt" "time" "122.51.31.227/go-course/go18/devcloud/mcenter/apps/namespace" "122.51.31.227/go-course/go18/devcloud/mcenter/apps/role" "122.51.31.227/go-course/go18/devcloud/mcenter/apps/user" - "github.com/infraboard/mcube/v2/ioc/config/datasource" "github.com/infraboard/mcube/v2/ioc/config/validator" "github.com/infraboard/mcube/v2/tools/pretty" "github.com/infraboard/modules/iam/apps" + "gorm.io/datatypes" "gorm.io/gorm" + "gorm.io/gorm/clause" ) func NewPolicy() *Policy { @@ -82,10 +81,11 @@ type ResourceScope struct { Scope map[string][]string `json:"scope" bson:"scope" gorm:"column:scope;serializer:json;type:json" description:"数据访问的范围" optional:"true"` } -// 辅助函数:将字符串切片转换为 JSON 数组字符串 -func toJsonArray(arr []string) string { - b, _ := json.Marshal(arr) - return string(b) +func (l *ResourceScope) SetScope(key string, value []string) { + if l.Scope == nil { + l.Scope = map[string][]string{} + } + l.Scope[key] = value } func (r ResourceScope) GormResourceFilter(query *gorm.DB) *gorm.DB { @@ -93,23 +93,27 @@ func (r ResourceScope) GormResourceFilter(query *gorm.DB) *gorm.DB { query = query.Where("namespace = ?", r.NamespaceId) } - switch datasource.Get().Provider { - case datasource.PROVIDER_POSTGRES: - for key, values := range r.Scope { - for k, v := range r.Scope { - // 创建一个临时 JSON 对象 {"key": ["value1", "value2"]} - jsonCondition := fmt.Sprintf(`{"%s": %s}`, k, toJsonArray(v)) - query = query.Where("label @> ?", jsonCondition) - } - query = query.Where("label -->>? IN ?", key, values) + for key, values := range r.Scope { + if len(values) == 0 { + continue } - case datasource.PROVIDER_MYSQL: - // 过滤条件, Label - for key, values := range r.Scope { - query = query.Where("label->>? IN (?)", "$."+key, values) - } - } + // 构建"标签不存在"条件 + notHasKey := clause.Not(datatypes.JSONQuery("label").HasKey(key)) + + // 构建"标签值匹配"条件 + var valueMatches []clause.Expression + for _, val := range values { + valueMatches = append(valueMatches, + datatypes.JSONQuery("label").Equals(val, key)) + } + + // 组合条件:标签不存在 OR 标签值匹配 + query = query.Where(clause.Or( + notHasKey, + clause.Or(valueMatches...), + )) + } return query } @@ -128,3 +132,10 @@ type ResourceLabel struct { // 访问范围, 需要提前定义scope, 比如环境, 后端开发小组,开发资源 Label map[string]string `json:"label" bson:"label" gorm:"column:label;serializer:json;type:json" description:"数据访问的范围" optional:"true"` } + +func (l *ResourceLabel) SetLabel(key, value string) { + if l.Label == nil { + l.Label = map[string]string{} + } + l.Label[key] = value +} diff --git a/devcloud/mpaas/apps/application/README.md b/devcloud/mpaas/apps/application/README.md index 0e52a5c..643bb4c 100644 --- a/devcloud/mpaas/apps/application/README.md +++ b/devcloud/mpaas/apps/application/README.md @@ -1,3 +1,9 @@ # 应用管理 -应用的核心是代码, 围绕这应用的核心代码, SCM的仓库地址管理 \ No newline at end of file +应用的核心是代码, 围绕这应用的核心代码, SCM的仓库地址管理 + + +## 应用CRUD + +最麻烦的基于标签的动态匹配(核心权限) + diff --git a/devcloud/mpaas/apps/application/impl/application.go b/devcloud/mpaas/apps/application/impl/application.go index ebe5a61..f9b4eaa 100644 --- a/devcloud/mpaas/apps/application/impl/application.go +++ b/devcloud/mpaas/apps/application/impl/application.go @@ -4,8 +4,10 @@ import ( "context" "122.51.31.227/go-course/go18/devcloud/mpaas/apps/application" + "github.com/infraboard/mcube/v2/exception" "github.com/infraboard/mcube/v2/ioc/config/datasource" "github.com/infraboard/mcube/v2/types" + "gorm.io/gorm" ) // CreateApplication implements application.Service. @@ -27,7 +29,7 @@ func (i *ApplicationServiceImpl) CreateApplication(ctx context.Context, in *appl func (i *ApplicationServiceImpl) QueryApplication(ctx context.Context, in *application.QueryApplicationRequest) (*types.Set[*application.Application], error) { set := types.New[*application.Application]() - query := datasource.DBFromCtx(ctx).Model(&application.Application{}) + query := in.GormResourceFilter(datasource.DBFromCtx(ctx).Model(&application.Application{})) if in.Id != "" { query = query.Where("id = ?", in.Id) } @@ -64,16 +66,40 @@ func (i *ApplicationServiceImpl) QueryApplication(ctx context.Context, in *appli } // DescribeApplication implements application.Service. -func (i *ApplicationServiceImpl) DescribeApplication(context.Context, *application.DescribeApplicationRequest) (*application.Application, error) { - panic("unimplemented") +func (i *ApplicationServiceImpl) DescribeApplication(ctx context.Context, in *application.DescribeApplicationRequest) (*application.Application, error) { + query := in.GormResourceFilter(datasource.DBFromCtx(ctx).Model(&application.Application{})) + + ins := &application.Application{} + if err := query.Where("id = ?", in.Id).First(ins).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, exception.NewNotFound("app %s not found", in.Id) + } + return nil, err + } + + return ins, nil } // UpdateApplication implements application.Service. -func (i *ApplicationServiceImpl) UpdateApplication(context.Context, *application.UpdateApplicationRequest) (*application.Application, error) { - panic("unimplemented") +func (i *ApplicationServiceImpl) UpdateApplication(ctx context.Context, in *application.UpdateApplicationRequest) (*application.Application, error) { + ins, err := i.DescribeApplication(ctx, &in.DescribeApplicationRequest) + if err != nil { + return nil, err + } + + ins.CreateApplicationSpec = in.CreateApplicationSpec + return ins, datasource.DBFromCtx(ctx).Where("id = ?", in.Id).Updates(ins).Error } // DeleteApplication implements application.Service. -func (i *ApplicationServiceImpl) DeleteApplication(context.Context, *application.DeleteApplicationRequest) (*application.Application, error) { - panic("unimplemented") +func (i *ApplicationServiceImpl) DeleteApplication(ctx context.Context, in *application.DeleteApplicationRequest) (*application.Application, error) { + ins, err := i.DescribeApplication(ctx, &in.DescribeApplicationRequest) + if err != nil { + return nil, err + } + + return ins, datasource.DBFromCtx(ctx). + Where("id = ?", in.Id). + Delete(&application.Application{}). + Error } diff --git a/devcloud/mpaas/apps/application/impl/application_test.go b/devcloud/mpaas/apps/application/impl/application_test.go index 4afcb9b..ea33398 100644 --- a/devcloud/mpaas/apps/application/impl/application_test.go +++ b/devcloud/mpaas/apps/application/impl/application_test.go @@ -14,9 +14,22 @@ func TestCreateApplication(t *testing.T) { req.CodeRepository = application.CodeRepository{ SshUrl: "git@122.51.31.227:go-course/go18.git", } + req.SetLabel("team", "golang_dev_group_01") ins, err := svc.CreateApplication(ctx, req) if err != nil { t.Fatal(err) } t.Log(ins) } + +// SELECT * FROM `applications` WHERE (NOT JSON_EXTRACT(`label`,'$.team') IS NOT NULL OR JSON_EXTRACT(`label`,'$.team') = 'golang_dev_group_01') ORDER BY created_at desc LIMIT 20 +func TestQueryApplication(t *testing.T) { + req := application.NewQueryApplicationRequest() + req.SetScope("team", []string{"golang_dev_group_01"}) + // req.SetScope("env", []string{"prod"}) + ins, err := svc.QueryApplication(ctx, req) + if err != nil { + t.Fatal(err) + } + t.Log(ins) +} diff --git a/devcloud/mpaas/apps/application/interface.go b/devcloud/mpaas/apps/application/interface.go index 92a3e8e..ad0d4be 100644 --- a/devcloud/mpaas/apps/application/interface.go +++ b/devcloud/mpaas/apps/application/interface.go @@ -30,6 +30,14 @@ type Service interface { DescribeApplication(context.Context, *DescribeApplicationRequest) (*Application, error) } +func NewQueryApplicationRequest() *QueryApplicationRequest { + return &QueryApplicationRequest{ + QueryApplicationRequestSpec: QueryApplicationRequestSpec{ + PageRequest: request.NewDefaultPageRequest(), + }, + } +} + type QueryApplicationRequest struct { policy.ResourceScope QueryApplicationRequestSpec diff --git a/go.mod b/go.mod index fe95c63..eca946a 100644 --- a/go.mod +++ b/go.mod @@ -17,11 +17,13 @@ require ( go.mongodb.org/mongo-driver v1.17.3 golang.org/x/crypto v0.38.0 gopkg.in/yaml.v3 v3.0.1 + gorm.io/datatypes v1.2.5 gorm.io/driver/mysql v1.5.7 gorm.io/gorm v1.26.0 ) require ( + filippo.io/edwards25519 v1.1.0 // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.13.2 // indirect @@ -44,13 +46,13 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.26.0 // indirect - github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect diff --git a/go.sum b/go.sum index df0ac9e..3ce50fc 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -66,11 +68,16 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -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-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -93,8 +100,8 @@ github.com/infraboard/modules v0.0.12 h1:vQqm+JwzmhL+hcD9SV+WVlp9ecInc7NsbGahcTm github.com/infraboard/modules v0.0.12/go.mod h1:NdgdH/NoeqibJmFPn9th+tisMuR862/crbXeH4FPMaU= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= +github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= @@ -136,6 +143,10 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA= +github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -317,10 +328,16 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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/datatypes v1.2.5 h1:9UogU3jkydFVW1bIVVeoYsTpLRgwDVW3rHfJG6/Ek9I= +gorm.io/datatypes v1.2.5/go.mod h1:I5FUdlKpLb5PMqeMQhm30CQ6jXP8Rj89xkTeCSAaAD4= gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= +gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= +gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g= +gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g= gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.26.0 h1:9lqQVPG5aNNS6AyHdRiwScAVnXHg/L/Srzx55G5fOgs= gorm.io/gorm v1.26.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=