fix: recode project-role rpc handle to support group role assignment

results by user or project
This commit is contained in:
Qiu Jian
2020-09-09 00:02:06 +08:00
parent 6ea5995242
commit 332ba04bd1
13 changed files with 638 additions and 283 deletions

View File

@@ -23,20 +23,25 @@ import (
func init() {
type RoleAssignmentsOptions struct {
Effective bool `help:"Include role assignment of group members"`
System bool `help:"Include system user account"`
Policy bool `help:"Show matched policies"`
Domain string `help:"Role assignments for domain"`
User string `help:"For user"`
UserDomain string `help:"Domain for user"`
Group string `help:"For group"`
GroupDomain string `help:"Domain for group"`
Project string `help:"Role assignments for project"`
ProjectDomain string `help:"Domain for project"`
Role string `help:"Role assignments for role"`
RoleDomain string `help:"Domain for role"`
Limit int64 `help:"maximal returned number of rows"`
Offset int64 `help:"offset index of returned results"`
Effective bool `help:"Include role assignment of group members"`
System bool `help:"Include system user account"`
Policy bool `help:"Show matched policies"`
Domain string `help:"Role assignments for domain"`
User string `help:"For user"`
UserDomain string `help:"Domain for user"`
Group string `help:"For group"`
GroupDomain string `help:"Domain for group"`
Project string `help:"Role assignments for project"`
ProjectDomain string `help:"Domain for project"`
Role string `help:"Role assignments for role"`
RoleDomain string `help:"Domain for role"`
Limit int64 `help:"maximal returned number of rows"`
Offset int64 `help:"offset index of returned results"`
Users []string `help:"fitler by users id or name"`
Groups []string `help:"fitler by users id or name"`
Roles []string `help:"fitler by users id or name"`
Projects []string `help:"fitler by users id or name"`
Domains []string `help:"fitler by users id or name"`
}
R(&RoleAssignmentsOptions{}, "role-assignments", "List all role assignments", func(s *mcclient.ClientSession, args *RoleAssignmentsOptions) error {
query := jsonutils.NewDict()
@@ -85,6 +90,21 @@ func init() {
}
query.Add(jsonutils.NewString(rid), "role", "id")
}
if len(args.Users) > 0 {
query.Add(jsonutils.NewStringArray(args.Users), "users")
}
if len(args.Groups) > 0 {
query.Add(jsonutils.NewStringArray(args.Groups), "groups")
}
if len(args.Roles) > 0 {
query.Add(jsonutils.NewStringArray(args.Roles), "roles")
}
if len(args.Projects) > 0 {
query.Add(jsonutils.NewStringArray(args.Projects), "projects")
}
if len(args.Domains) > 0 {
query.Add(jsonutils.NewStringArray(args.Domains), "domains")
}
if args.Limit > 0 {
query.Add(jsonutils.NewInt(args.Limit), "limit")
}

2
go.mod
View File

@@ -146,7 +146,7 @@ require (
yunion.io/x/ovsdb v0.0.0-20200526071744-27bf0940cbc7
yunion.io/x/pkg v0.0.0-20200814072949-4f1b541857d6
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e
yunion.io/x/sqlchemy v0.0.0-20200820050957-1542f05b9fcd
yunion.io/x/sqlchemy v0.0.0-20200909073353-606d1fe76db5
yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce
)

4
go.sum
View File

@@ -1139,7 +1139,7 @@ yunion.io/x/pkg v0.0.0-20200814072949-4f1b541857d6 h1:UarEDTBGkgcgc+nc+PZ75uo9M9
yunion.io/x/pkg v0.0.0-20200814072949-4f1b541857d6/go.mod h1:t6rEGG2sQ4J7DhFxSZVOTjNd0YO/KlfWQyK1W4tog+E=
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e h1:v+EzIadodSwkdZ/7bremd7J8J50Cise/HCylsOJngmo=
yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e/go.mod h1:0iFKpOs1y4lbCxeOmq3Xx/0AcQoewVPwj62eRluioEo=
yunion.io/x/sqlchemy v0.0.0-20200820050957-1542f05b9fcd h1:G5yfI0i4Du8olStezNPWRWdmSS4cbj1wZfB11HhulQc=
yunion.io/x/sqlchemy v0.0.0-20200820050957-1542f05b9fcd/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI=
yunion.io/x/sqlchemy v0.0.0-20200909073353-606d1fe76db5 h1:Z1BoBr1LUEqQKJ5k+xTczj22OSiOvMxEN5BJrOQq7+g=
yunion.io/x/sqlchemy v0.0.0-20200909073353-606d1fe76db5/go.mod h1:FTdwPdGhMgh4E+UFXc9klI1Ok34fMuybTT+jLhOaIjI=
yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce h1:kU8xE7O5uZ1GSJVMZHoJ+jrNL7csUQHYGyAPW9QfNpE=
yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce/go.mod h1:EP6NSv2C0zzqBDTKumv8hPWLb3XvgMZDHQRfyuOrQng=

View File

@@ -66,3 +66,41 @@ func (ra *SRoleAssignment) GetLoginIp() string {
func (ra *SRoleAssignment) GetTokenString() string {
return "faketoken"
}
type RAInputObject struct {
Id string `json:"id"`
}
type RoleAssignmentsInput struct {
User RAInputObject `json:"user"`
Group RAInputObject `json:"group"`
Role RAInputObject `json:"role"`
Scope struct {
Project RAInputObject `json:"project"`
Domain RAInputObject `json:"domain"`
} `json:"scope"`
Users []string `json:"users"`
Groups []string `json:"groups"`
Roles []string `json:"roles"`
Projects []string `json:"projects"`
Domains []string `json:"domains"`
IncludeNames *bool `json:"include_names"`
Effective *bool `json:"effective"`
IncludeSubtree *bool `json:"include_subtree"`
IncludeSystem *bool `json:"include_system"`
IncludePolicies *bool `json:"include_policies"`
Limit *int `json:"limit"`
Offset *int `json:"offset"`
}
type RoleAssignmentsOutput struct {
RoleAssignments []SRoleAssignment `json:"role_assignments,allowempty"`
Total int64 `json:"total"`
Limit int `json:"limit"`
Offset int `json:"offset"`
}

View File

@@ -430,37 +430,59 @@ func AddAdhocHandlers(version string, app *appsrv.Application) {
func roleAssignmentHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
_, query, _ := appsrv.FetchEnv(ctx, w, r)
userId, _ := query.GetString("user", "id")
groupId, _ := query.GetString("group", "id")
roleId, _ := query.GetString("role", "id")
domainId, _ := query.GetString("scope", "domain", "id")
projectId, _ := query.GetString("scope", "project", "id")
includeNames := query.Contains("include_names")
effective := query.Contains("effective")
includeSub := query.Contains("include_subtree")
includeSystem := query.Contains("include_system")
includePolicies := query.Contains("include_policies")
limit, _ := query.Int("limit")
offset, _ := query.Int("offset")
results, total, err := AssignmentManager.FetchAll(userId, groupId, roleId, domainId, projectId, includeNames, effective, includeSub, includeSystem, includePolicies, int(limit), int(offset))
input := api.RoleAssignmentsInput{}
err := query.Unmarshal(&input)
if err != nil {
httperrors.GeneralServerError(ctx, w, err)
return
}
body := jsonutils.NewDict()
body.Add(jsonutils.Marshal(results), "role_assignments")
body.Add(jsonutils.NewInt(total), "total")
if limit > 0 {
body.Add(jsonutils.NewInt(limit), "limit")
includeNames := (input.IncludeNames != nil)
effective := (input.Effective != nil)
includeSub := (input.IncludeSubtree != nil)
includeSystem := (input.IncludeSystem != nil)
includePolicies := (input.IncludePolicies != nil)
limit := 0
if input.Limit != nil {
limit = *input.Limit
}
if offset > 0 {
body.Add(jsonutils.NewInt(offset), "offset")
offset := 0
if input.Offset != nil {
offset = *input.Offset
}
appsrv.SendJSON(w, body)
results, total, err := AssignmentManager.FetchAll(
input.User.Id,
input.Group.Id,
input.Role.Id,
input.Scope.Domain.Id,
input.Scope.Project.Id,
input.Users,
input.Groups,
input.Roles,
input.Domains,
input.Projects,
includeNames, effective, includeSub, includeSystem, includePolicies,
limit, offset)
if err != nil {
httperrors.GeneralServerError(ctx, w, err)
return
}
output := api.RoleAssignmentsOutput{}
output.RoleAssignments = results
output.Total = total
output.Limit = limit
output.Offset = offset
appsrv.SendJSON(w, jsonutils.Marshal(output))
}
func (manager *SAssignmentManager) queryAll(userId, groupId, roleId, domainId, projectId string) *sqlchemy.SQuery {
func (manager *SAssignmentManager) queryAll(
userId, groupId, roleId, domainId, projectId string,
users, groups, roles, domains, projects []string,
) *sqlchemy.SQuery {
assigments := manager.Query().SubQuery()
q := assigments.Query(
assigments.Field("type"),
@@ -499,18 +521,58 @@ func (manager *SAssignmentManager) queryAll(userId, groupId, roleId, domainId, p
if len(userId) > 0 {
q = q.In("type", []string{api.AssignmentUserProject, api.AssignmentUserDomain}).Equals("user_id", userId)
}
if len(users) > 0 {
subq := UserManager.Query("id")
subq = subq.Filter(sqlchemy.OR(
sqlchemy.In(subq.Field("id"), users),
sqlchemy.ContainsAny(subq.Field("name"), users),
))
q = q.In("type", []string{api.AssignmentUserProject, api.AssignmentUserDomain}).In("user_id", subq.SubQuery())
}
if len(groupId) > 0 {
q = q.In("type", []string{api.AssignmentGroupProject, api.AssignmentGroupDomain}).Equals("group_id", groupId)
}
if len(groups) > 0 {
subq := GroupManager.Query("id")
subq = subq.Filter(sqlchemy.OR(
sqlchemy.In(subq.Field("id"), groups),
sqlchemy.ContainsAny(subq.Field("name"), groups),
))
q = q.In("type", []string{api.AssignmentGroupProject, api.AssignmentGroupDomain}).In("group_id", subq.SubQuery())
}
if len(roleId) > 0 {
q = q.Equals("role_id", roleId)
}
if len(roles) > 0 {
subq := RoleManager.Query("id")
subq = subq.Filter(sqlchemy.OR(
sqlchemy.In(subq.Field("id"), roles),
sqlchemy.ContainsAny(subq.Field("name"), roles),
))
q = q.In("role_id", subq.SubQuery())
}
if len(projectId) > 0 {
q = q.Equals("project_id", projectId).In("type", []string{api.AssignmentUserProject, api.AssignmentGroupProject})
}
if len(projects) > 0 {
subq := ProjectManager.Query("id")
subq = subq.Filter(sqlchemy.OR(
sqlchemy.In(subq.Field("id"), projects),
sqlchemy.ContainsAny(subq.Field("name"), projects),
))
q = q.In("project_id", subq.SubQuery()).In("type", []string{api.AssignmentUserProject, api.AssignmentGroupProject})
}
if len(domainId) > 0 {
q = q.Equals("domain_id", domainId).In("type", []string{api.AssignmentUserDomain, api.AssignmentGroupDomain})
}
if len(domains) > 0 {
subq := DomainManager.Query("id")
subq = subq.Filter(sqlchemy.OR(
sqlchemy.In(subq.Field("id"), domains),
sqlchemy.ContainsAny(subq.Field("name"), domains),
))
q = q.In("domain_id", subq.SubQuery()).In("type", []string{api.AssignmentUserDomain, api.AssignmentGroupDomain})
}
return q
}
@@ -562,14 +624,18 @@ func (assign *sAssignmentInternal) getRoleAssignment(domains, projects, groups,
return ra
}
func (manager *SAssignmentManager) FetchAll(userId, groupId, roleId, domainId, projectId string, includeNames, effective, includeSub, includeSystem, includePolicies bool, limit, offset int) ([]api.SRoleAssignment, int64, error) {
func (manager *SAssignmentManager) FetchAll(
userId, groupId, roleId, domainId, projectId string,
userStrs, groupStrs, roleStrs, domainStrs, projectStrs []string,
includeNames, effective, includeSub, includeSystem, includePolicies bool,
limit, offset int) ([]api.SRoleAssignment, int64, error) {
var q *sqlchemy.SQuery
if effective {
usrq := manager.queryAll(userId, "", roleId, domainId, projectId).In("type", []string{api.AssignmentUserProject, api.AssignmentUserDomain})
usrq := manager.queryAll(userId, "", roleId, domainId, projectId, userStrs, nil, roleStrs, domainStrs, projectStrs).In("type", []string{api.AssignmentUserProject, api.AssignmentUserDomain})
memberships := UsergroupManager.Query("user_id", "group_id").SubQuery()
grpproj := manager.queryAll("", groupId, roleId, domainId, projectId).In("type", []string{api.AssignmentGroupProject, api.AssignmentGroupDomain}).SubQuery()
grpproj := manager.queryAll("", groupId, roleId, domainId, projectId, nil, groupStrs, roleStrs, domainStrs, projectStrs).In("type", []string{api.AssignmentGroupProject, api.AssignmentGroupDomain}).SubQuery()
q2 := grpproj.Query(
grpproj.Field("type"),
memberships.Field("user_id"),
@@ -585,7 +651,7 @@ func (manager *SAssignmentManager) FetchAll(userId, groupId, roleId, domainId, p
q = sqlchemy.Union(usrq, q2).Query().Distinct()
} else {
q = manager.queryAll(userId, groupId, roleId, domainId, projectId).Distinct()
q = manager.queryAll(userId, groupId, roleId, domainId, projectId, userStrs, groupStrs, roleStrs, domainStrs, projectStrs).Distinct()
}
if !includeSystem {

View File

@@ -291,7 +291,9 @@ func (t *SAuthToken) getTokenV3(
token.Token.Projects[i].Domain.Id = extProjs[i].DomainId
token.Token.Projects[i].Domain.Name = extProjs[i].DomainName
}*/
assigns, _, err := models.AssignmentManager.FetchAll(user.Id, "", "", "", "", true, true, true, true, true, 0, 0)
assigns, _, err := models.AssignmentManager.FetchAll(user.Id, "", "", "", "",
nil, nil, nil, nil, nil,
true, true, true, true, true, 0, 0)
if err != nil {
return nil, errors.Wrap(err, "models.AssignmentManager.FetchAll")
}

View File

@@ -275,7 +275,7 @@ func (manager *SCredentialManager) CreateAccessKeySecret(s *mcclient.ClientSessi
return aksk, nil
}
func (manager *SCredentialManager) DoCreateOIDCSecret(s *mcclient.ClientSession, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
func (manager *SCredentialManager) DoCreateOidcSecret(s *mcclient.ClientSession, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
redirectUri, _ := params.GetString("redirect_uri")
key, err := manager.CreateOIDCSecret(s, "", "", redirectUri)

View File

@@ -18,8 +18,10 @@ import (
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/pkg/errors"
"yunion.io/x/pkg/utils"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modulebase"
)
@@ -130,13 +132,12 @@ var (
RoleAssignments RoleAssignmentManagerV3
)
// get project users for given project
// get users for given project
func (this *RoleAssignmentManagerV3) GetProjectUsers(s *mcclient.ClientSession, id string, params jsonutils.JSONObject) (jsonutils.JSONObject, error) {
query := jsonutils.NewDict()
effective, e := params.GetString("effective")
if e == nil && effective == "true" {
if params.Contains("effective") {
query.Add(jsonutils.JSONNull, "effective")
}
@@ -170,6 +171,8 @@ func (this *RoleAssignmentManagerV3) GetProjectUsers(s *mcclient.ClientSession,
query.Add(jsonutils.NewString(id), resource, "id")
}
query.Add(jsonutils.JSONNull, "include_policies")
result, err := this.List(s, query)
if err != nil {
return jsonutils.JSONNull, err
@@ -217,8 +220,7 @@ func (this *RoleAssignmentManagerV3) GetProjectRole(s *mcclient.ClientSession, i
data := jsonutils.NewDict()
query := jsonutils.NewDict()
effective, e := params.GetString("effective")
if e == nil && effective == "true" {
if params.Contains("effective") {
query.Add(jsonutils.JSONNull, "effective")
}
@@ -242,6 +244,27 @@ func (this *RoleAssignmentManagerV3) GetProjectRole(s *mcclient.ClientSession, i
return jsonutils.JSONNull, fmt.Errorf("not allowed resource %s", resource)
}
// search by project id or name
searchProjs := jsonutils.GetQueryStringArray(params, "projects")
if len(searchProjs) > 0 {
query.Add(jsonutils.NewStringArray(searchProjs), "projects")
}
// search by user id or name
searchUsers := jsonutils.GetQueryStringArray(params, "users")
if len(searchUsers) > 0 {
query.Add(jsonutils.NewStringArray(searchUsers), "users")
}
groupBy, _ := params.GetString("group_by")
if len(groupBy) == 0 {
groupBy = "project"
}
if groupBy == "project" {
} else if groupBy == "user" {
} else {
return nil, errors.Wrapf(httperrors.ErrInputParameter, "unsupported group_by value %s", groupBy)
}
query.Add(jsonutils.JSONNull, "include_names")
if scope {
@@ -256,43 +279,53 @@ func (this *RoleAssignmentManagerV3) GetProjectRole(s *mcclient.ClientSession, i
return jsonutils.JSONNull, err
}
projects := make([]sProjectGroupRole, 0)
lines := make([]sProjectGroupRole, 0)
for _, roleAssign := range result.Data {
roleId, _ := roleAssign.GetString("role", "id")
roleName, _ := roleAssign.GetString("role", "name")
projectId, _ := roleAssign.GetString("scope", "project", "id")
projectName, _ := roleAssign.GetString("scope", "project", "name")
var groupById string
var groupByName string
if groupBy == "project" {
groupById, _ = roleAssign.GetString("scope", "project", "id")
groupByName, _ = roleAssign.GetString("scope", "project", "name")
} else if groupBy == "user" {
groupById, _ = roleAssign.GetString("user", "id")
groupByName, _ = roleAssign.GetString("user", "name")
}
groupId, _ := roleAssign.GetString("group", "id")
groupName, _ := roleAssign.GetString("group", "name")
projPolicies, _ := jsonutils.GetStringArray(roleAssign, "policies", "project")
domPolicies, _ := jsonutils.GetStringArray(roleAssign, "policies", "domain")
sysPolicies, _ := jsonutils.GetStringArray(roleAssign, "policies", "system")
projIdx := -1
for i := range projects {
if projects[i].Id == projectId {
projIdx = i
lineIdx := -1
for i := range lines {
if lines[i].Id == groupById {
lineIdx = i
break
}
}
if projIdx < 0 {
projIdx = len(projects)
projects = append(projects, sProjectGroupRole{
Id: projectId,
Name: projectName,
if lineIdx < 0 {
lineIdx = len(lines)
lines = append(lines, sProjectGroupRole{
Id: groupById,
Name: groupByName,
})
}
projects[projIdx].add(groupId, groupName, roleId, roleName, projPolicies, domPolicies, sysPolicies)
lines[lineIdx].add(groupId, groupName, roleId, roleName, projPolicies, domPolicies, sysPolicies)
}
projJson := jsonutils.NewArray()
for _, proj := range projects {
projJson.Add(jsonutils.Marshal(proj))
lineJson := jsonutils.NewArray()
for _, line := range lines {
lineJson.Add(jsonutils.Marshal(line))
}
data.Add(projJson, "data")
data.Add(jsonutils.NewInt(int64(len(projects))), "total")
data.Add(lineJson, "data")
data.Add(jsonutils.NewInt(int64(len(lines))), "total")
return data, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,22 +14,22 @@ const (
// ErrCodeConflictingDomainExists for service response error code
// "ConflictingDomainExists".
//
// The cause of this error depends on whether you're trying to create a public
// or a private hosted zone:
// The cause of this error depends on the operation that you're performing:
//
// * Public hosted zone: Two hosted zones that have the same name or that
// have a parent/child relationship (example.com and test.example.com) can't
// have any common name servers. You tried to create a hosted zone that has
// the same name as an existing hosted zone or that's the parent or child
// of an existing hosted zone, and you specified a delegation set that shares
// one or more name servers with the existing hosted zone. For more information,
// see CreateReusableDelegationSet (https://docs.aws.amazon.com/Route53/latest/APIReference/API_CreateReusableDelegationSet.html).
// * Create a public hosted zone: Two hosted zones that have the same name
// or that have a parent/child relationship (example.com and test.example.com)
// can't have any common name servers. You tried to create a hosted zone
// that has the same name as an existing hosted zone or that's the parent
// or child of an existing hosted zone, and you specified a delegation set
// that shares one or more name servers with the existing hosted zone. For
// more information, see CreateReusableDelegationSet (https://docs.aws.amazon.com/Route53/latest/APIReference/API_CreateReusableDelegationSet.html).
//
// * Private hosted zone: You specified an Amazon VPC that you're already
// using for another hosted zone, and the domain that you specified for one
// of the hosted zones is a subdomain of the domain that you specified for
// the other hosted zone. For example, you can't use the same Amazon VPC
// for the hosted zones for example.com and test.example.com.
// * Create a private hosted zone: A hosted zone with the specified name
// already exists and is already associated with the Amazon VPC that you
// specified.
//
// * Associate VPCs with a private hosted zone: The VPC that you specified
// is already associated with another hosted zone that has the same name.
ErrCodeConflictingDomainExists = "ConflictingDomainExists"
// ErrCodeConflictingTypes for service response error code
@@ -240,7 +240,9 @@ const (
// ErrCodeNoSuchGeoLocation for service response error code
// "NoSuchGeoLocation".
//
// Amazon Route 53 doesn't support the specified geographic location.
// Amazon Route 53 doesn't support the specified geographic location. For a
// list of supported geolocation codes, see the GeoLocation (https://docs.aws.amazon.com/Route53/latest/APIReference/API_GeoLocation.html)
// data type.
ErrCodeNoSuchGeoLocation = "NoSuchGeoLocation"
// ErrCodeNoSuchHealthCheck for service response error code

View File

@@ -31,7 +31,7 @@ var initRequest func(*request.Request)
const (
ServiceName = "route53" // Name of service.
EndpointsID = ServiceName // ID to lookup a service endpoint with.
ServiceID = "Route 53" // ServiceID is a unique identifer of a specific service.
ServiceID = "Route 53" // ServiceID is a unique identifier of a specific service.
)
// New creates a new instance of the Route53 client with a session.
@@ -39,6 +39,8 @@ const (
// aws.Config parameter to add your extra config.
//
// Example:
// mySession := session.Must(session.NewSession())
//
// // Create a Route53 client from just a session.
// svc := route53.New(mySession)
//
@@ -46,11 +48,11 @@ const (
// svc := route53.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *Route53 {
c := p.ClientConfig(EndpointsID, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName)
return newClient(*c.Config, c.Handlers, c.PartitionID, c.Endpoint, c.SigningRegion, c.SigningName)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *Route53 {
func newClient(cfg aws.Config, handlers request.Handlers, partitionID, endpoint, signingRegion, signingName string) *Route53 {
svc := &Route53{
Client: client.New(
cfg,
@@ -59,6 +61,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
ServiceID: ServiceID,
SigningName: signingName,
SigningRegion: signingRegion,
PartitionID: partitionID,
Endpoint: endpoint,
APIVersion: "2013-04-01",
},

2
vendor/modules.txt vendored
View File

@@ -1109,7 +1109,7 @@ yunion.io/x/pkg/util/workqueue
yunion.io/x/pkg/utils
# yunion.io/x/s3cli v0.0.0-20190917004522-13ac36d8687e
yunion.io/x/s3cli
# yunion.io/x/sqlchemy v0.0.0-20200820050957-1542f05b9fcd
# yunion.io/x/sqlchemy v0.0.0-20200909073353-606d1fe76db5
yunion.io/x/sqlchemy
# yunion.io/x/structarg v0.0.0-20200720093445-9f850fa222ce
yunion.io/x/structarg

View File

@@ -378,6 +378,14 @@ func Like(f IQueryField, v string) ICondition {
return &c
}
func ContainsAny(f IQueryField, v []string) ICondition {
conds := make([]ICondition, len(v))
for i := range v {
conds[i] = Contains(f, v[i])
}
return OR(conds...)
}
func Contains(f IQueryField, v string) ICondition {
v = likeEscape(v)
nv := fmt.Sprintf("%%%s%%", v)