unlock multiple storage policy

pull/3493/head
thotenn 3 weeks ago
parent 1d091c4100
commit 7f11ec9e88

@ -3,7 +3,9 @@ package constants
// These values will be injected at build time, DO NOT EDIT.
// BackendVersion 当前后端版本号
var BackendVersion = "4.14.0"
// Bumped to 4.14.1 in this fork to trigger the schema migration that creates the
// group⇄storage_policy allowed-set join table and backfills it (see inventory/migration.go).
var BackendVersion = "4.14.1"
// IsPro 是否为Pro版本
var IsPro = "false"

@ -1369,6 +1369,22 @@ func (c *GroupClient) QueryStoragePolicies(gr *Group) *StoragePolicyQuery {
return query
}
// QueryStoragePoliciesAllowed queries the storage_policies_allowed edge of a Group.
func (c *GroupClient) QueryStoragePoliciesAllowed(gr *Group) *StoragePolicyQuery {
query := (&StoragePolicyClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := gr.ID
step := sqlgraph.NewStep(
sqlgraph.From(group.Table, group.FieldID, id),
sqlgraph.To(storagepolicy.Table, storagepolicy.FieldID),
sqlgraph.Edge(sqlgraph.M2M, false, group.StoragePoliciesAllowedTable, group.StoragePoliciesAllowedPrimaryKey...),
)
fromV = sqlgraph.Neighbors(gr.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *GroupClient) Hooks() []Hook {
hooks := c.hooks.Group
@ -2641,6 +2657,22 @@ func (c *StoragePolicyClient) QueryNode(sp *StoragePolicy) *NodeQuery {
return query
}
// QueryGroupsAllowed queries the groups_allowed edge of a StoragePolicy.
func (c *StoragePolicyClient) QueryGroupsAllowed(sp *StoragePolicy) *GroupQuery {
query := (&GroupClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := sp.ID
step := sqlgraph.NewStep(
sqlgraph.From(storagepolicy.Table, storagepolicy.FieldID, id),
sqlgraph.To(group.Table, group.FieldID),
sqlgraph.Edge(sqlgraph.M2M, true, storagepolicy.GroupsAllowedTable, storagepolicy.GroupsAllowedPrimaryKey...),
)
fromV = sqlgraph.Neighbors(sp.driver.Dialect(), step)
return fromV, nil
}
return query
}
// Hooks returns the client hooks.
func (c *StoragePolicyClient) Hooks() []Hook {
hooks := c.hooks.StoragePolicy

@ -51,9 +51,11 @@ type GroupEdges struct {
Users []*User `json:"users,omitempty"`
// StoragePolicies holds the value of the storage_policies edge.
StoragePolicies *StoragePolicy `json:"storage_policies,omitempty"`
// StoragePoliciesAllowed holds the value of the storage_policies_allowed edge.
StoragePoliciesAllowed []*StoragePolicy `json:"storage_policies_allowed,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [2]bool
loadedTypes [3]bool
}
// UsersOrErr returns the Users value or an error if the edge
@ -78,6 +80,15 @@ func (e GroupEdges) StoragePoliciesOrErr() (*StoragePolicy, error) {
return nil, &NotLoadedError{edge: "storage_policies"}
}
// StoragePoliciesAllowedOrErr returns the StoragePoliciesAllowed value or an error if the edge
// was not loaded in eager-loading.
func (e GroupEdges) StoragePoliciesAllowedOrErr() ([]*StoragePolicy, error) {
if e.loadedTypes[2] {
return e.StoragePoliciesAllowed, nil
}
return nil, &NotLoadedError{edge: "storage_policies_allowed"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*Group) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
@ -194,6 +205,11 @@ func (gr *Group) QueryStoragePolicies() *StoragePolicyQuery {
return NewGroupClient(gr.config).QueryStoragePolicies(gr)
}
// QueryStoragePoliciesAllowed queries the "storage_policies_allowed" edge of the Group entity.
func (gr *Group) QueryStoragePoliciesAllowed() *StoragePolicyQuery {
return NewGroupClient(gr.config).QueryStoragePoliciesAllowed(gr)
}
// Update returns a builder for updating this Group.
// Note that you need to call Group.Unwrap() before calling this method if this Group
// was returned from a transaction, and the transaction was committed or rolled back.
@ -261,5 +277,11 @@ func (e *Group) SetStoragePolicies(v *StoragePolicy) {
e.Edges.loadedTypes[1] = true
}
// SetStoragePoliciesAllowed manually set the edge as loaded state.
func (e *Group) SetStoragePoliciesAllowed(v []*StoragePolicy) {
e.Edges.StoragePoliciesAllowed = v
e.Edges.loadedTypes[2] = true
}
// Groups is a parsable slice of Group.
type Groups []*Group

@ -38,6 +38,8 @@ const (
EdgeUsers = "users"
// EdgeStoragePolicies holds the string denoting the storage_policies edge name in mutations.
EdgeStoragePolicies = "storage_policies"
// EdgeStoragePoliciesAllowed holds the string denoting the storage_policies_allowed edge name in mutations.
EdgeStoragePoliciesAllowed = "storage_policies_allowed"
// Table holds the table name of the group in the database.
Table = "groups"
// UsersTable is the table that holds the users relation/edge.
@ -54,6 +56,11 @@ const (
StoragePoliciesInverseTable = "storage_policies"
// StoragePoliciesColumn is the table column denoting the storage_policies relation/edge.
StoragePoliciesColumn = "storage_policy_id"
// StoragePoliciesAllowedTable is the table that holds the storage_policies_allowed relation/edge. The primary key declared below.
StoragePoliciesAllowedTable = "group_storage_policies_allowed"
// StoragePoliciesAllowedInverseTable is the table name for the StoragePolicy entity.
// It exists in this package in order to avoid circular dependency with the "storagepolicy" package.
StoragePoliciesAllowedInverseTable = "storage_policies"
)
// Columns holds all SQL columns for group fields.
@ -70,6 +77,12 @@ var Columns = []string{
FieldStoragePolicyID,
}
var (
// StoragePoliciesAllowedPrimaryKey and StoragePoliciesAllowedColumn2 are the table columns denoting the
// primary key for the storage_policies_allowed relation (M2M).
StoragePoliciesAllowedPrimaryKey = []string{"group_id", "storage_policy_id"}
)
// ValidColumn reports if the column name is valid (part of the table columns).
func ValidColumn(column string) bool {
for i := range Columns {
@ -161,6 +174,20 @@ func ByStoragePoliciesField(field string, opts ...sql.OrderTermOption) OrderOpti
sqlgraph.OrderByNeighborTerms(s, newStoragePoliciesStep(), sql.OrderByField(field, opts...))
}
}
// ByStoragePoliciesAllowedCount orders the results by storage_policies_allowed count.
func ByStoragePoliciesAllowedCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newStoragePoliciesAllowedStep(), opts...)
}
}
// ByStoragePoliciesAllowed orders the results by storage_policies_allowed terms.
func ByStoragePoliciesAllowed(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newStoragePoliciesAllowedStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
func newUsersStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
@ -175,3 +202,10 @@ func newStoragePoliciesStep() *sqlgraph.Step {
sqlgraph.Edge(sqlgraph.M2O, true, StoragePoliciesTable, StoragePoliciesColumn),
)
}
func newStoragePoliciesAllowedStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(StoragePoliciesAllowedInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2M, false, StoragePoliciesAllowedTable, StoragePoliciesAllowedPrimaryKey...),
)
}

@ -517,6 +517,29 @@ func HasStoragePoliciesWith(preds ...predicate.StoragePolicy) predicate.Group {
})
}
// HasStoragePoliciesAllowed applies the HasEdge predicate on the "storage_policies_allowed" edge.
func HasStoragePoliciesAllowed() predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.M2M, false, StoragePoliciesAllowedTable, StoragePoliciesAllowedPrimaryKey...),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasStoragePoliciesAllowedWith applies the HasEdge predicate on the "storage_policies_allowed" edge with a given conditions (other predicates).
func HasStoragePoliciesAllowedWith(preds ...predicate.StoragePolicy) predicate.Group {
return predicate.Group(func(s *sql.Selector) {
step := newStoragePoliciesAllowedStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.Group) predicate.Group {
return predicate.Group(sql.AndPredicates(predicates...))

@ -162,6 +162,21 @@ func (gc *GroupCreate) SetStoragePolicies(s *StoragePolicy) *GroupCreate {
return gc.SetStoragePoliciesID(s.ID)
}
// AddStoragePoliciesAllowedIDs adds the "storage_policies_allowed" edge to the StoragePolicy entity by IDs.
func (gc *GroupCreate) AddStoragePoliciesAllowedIDs(ids ...int) *GroupCreate {
gc.mutation.AddStoragePoliciesAllowedIDs(ids...)
return gc
}
// AddStoragePoliciesAllowed adds the "storage_policies_allowed" edges to the StoragePolicy entity.
func (gc *GroupCreate) AddStoragePoliciesAllowed(s ...*StoragePolicy) *GroupCreate {
ids := make([]int, len(s))
for i := range s {
ids[i] = s[i].ID
}
return gc.AddStoragePoliciesAllowedIDs(ids...)
}
// Mutation returns the GroupMutation object of the builder.
func (gc *GroupCreate) Mutation() *GroupMutation {
return gc.mutation
@ -333,6 +348,22 @@ func (gc *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
_node.StoragePolicyID = nodes[0]
_spec.Edges = append(_spec.Edges, edge)
}
if nodes := gc.mutation.StoragePoliciesAllowedIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: false,
Table: group.StoragePoliciesAllowedTable,
Columns: group.StoragePoliciesAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(storagepolicy.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec
}

@ -20,12 +20,13 @@ import (
// GroupQuery is the builder for querying Group entities.
type GroupQuery struct {
config
ctx *QueryContext
order []group.OrderOption
inters []Interceptor
predicates []predicate.Group
withUsers *UserQuery
withStoragePolicies *StoragePolicyQuery
ctx *QueryContext
order []group.OrderOption
inters []Interceptor
predicates []predicate.Group
withUsers *UserQuery
withStoragePolicies *StoragePolicyQuery
withStoragePoliciesAllowed *StoragePolicyQuery
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
@ -106,6 +107,28 @@ func (gq *GroupQuery) QueryStoragePolicies() *StoragePolicyQuery {
return query
}
// QueryStoragePoliciesAllowed chains the current query on the "storage_policies_allowed" edge.
func (gq *GroupQuery) QueryStoragePoliciesAllowed() *StoragePolicyQuery {
query := (&StoragePolicyClient{config: gq.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := gq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := gq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(group.Table, group.FieldID, selector),
sqlgraph.To(storagepolicy.Table, storagepolicy.FieldID),
sqlgraph.Edge(sqlgraph.M2M, false, group.StoragePoliciesAllowedTable, group.StoragePoliciesAllowedPrimaryKey...),
)
fromU = sqlgraph.SetNeighbors(gq.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first Group entity from the query.
// Returns a *NotFoundError when no Group was found.
func (gq *GroupQuery) First(ctx context.Context) (*Group, error) {
@ -293,13 +316,14 @@ func (gq *GroupQuery) Clone() *GroupQuery {
return nil
}
return &GroupQuery{
config: gq.config,
ctx: gq.ctx.Clone(),
order: append([]group.OrderOption{}, gq.order...),
inters: append([]Interceptor{}, gq.inters...),
predicates: append([]predicate.Group{}, gq.predicates...),
withUsers: gq.withUsers.Clone(),
withStoragePolicies: gq.withStoragePolicies.Clone(),
config: gq.config,
ctx: gq.ctx.Clone(),
order: append([]group.OrderOption{}, gq.order...),
inters: append([]Interceptor{}, gq.inters...),
predicates: append([]predicate.Group{}, gq.predicates...),
withUsers: gq.withUsers.Clone(),
withStoragePolicies: gq.withStoragePolicies.Clone(),
withStoragePoliciesAllowed: gq.withStoragePoliciesAllowed.Clone(),
// clone intermediate query.
sql: gq.sql.Clone(),
path: gq.path,
@ -328,6 +352,17 @@ func (gq *GroupQuery) WithStoragePolicies(opts ...func(*StoragePolicyQuery)) *Gr
return gq
}
// WithStoragePoliciesAllowed tells the query-builder to eager-load the nodes that are connected to
// the "storage_policies_allowed" edge. The optional arguments are used to configure the query builder of the edge.
func (gq *GroupQuery) WithStoragePoliciesAllowed(opts ...func(*StoragePolicyQuery)) *GroupQuery {
query := (&StoragePolicyClient{config: gq.config}).Query()
for _, opt := range opts {
opt(query)
}
gq.withStoragePoliciesAllowed = query
return gq
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
@ -406,9 +441,10 @@ func (gq *GroupQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Group,
var (
nodes = []*Group{}
_spec = gq.querySpec()
loadedTypes = [2]bool{
loadedTypes = [3]bool{
gq.withUsers != nil,
gq.withStoragePolicies != nil,
gq.withStoragePoliciesAllowed != nil,
}
)
_spec.ScanValues = func(columns []string) ([]any, error) {
@ -442,6 +478,15 @@ func (gq *GroupQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Group,
return nil, err
}
}
if query := gq.withStoragePoliciesAllowed; query != nil {
if err := gq.loadStoragePoliciesAllowed(ctx, query, nodes,
func(n *Group) { n.Edges.StoragePoliciesAllowed = []*StoragePolicy{} },
func(n *Group, e *StoragePolicy) {
n.Edges.StoragePoliciesAllowed = append(n.Edges.StoragePoliciesAllowed, e)
}); err != nil {
return nil, err
}
}
return nodes, nil
}
@ -504,6 +549,67 @@ func (gq *GroupQuery) loadStoragePolicies(ctx context.Context, query *StoragePol
}
return nil
}
func (gq *GroupQuery) loadStoragePoliciesAllowed(ctx context.Context, query *StoragePolicyQuery, nodes []*Group, init func(*Group), assign func(*Group, *StoragePolicy)) error {
edgeIDs := make([]driver.Value, len(nodes))
byID := make(map[int]*Group)
nids := make(map[int]map[*Group]struct{})
for i, node := range nodes {
edgeIDs[i] = node.ID
byID[node.ID] = node
if init != nil {
init(node)
}
}
query.Where(func(s *sql.Selector) {
joinT := sql.Table(group.StoragePoliciesAllowedTable)
s.Join(joinT).On(s.C(storagepolicy.FieldID), joinT.C(group.StoragePoliciesAllowedPrimaryKey[1]))
s.Where(sql.InValues(joinT.C(group.StoragePoliciesAllowedPrimaryKey[0]), edgeIDs...))
columns := s.SelectedColumns()
s.Select(joinT.C(group.StoragePoliciesAllowedPrimaryKey[0]))
s.AppendSelect(columns...)
s.SetDistinct(false)
})
if err := query.prepareQuery(ctx); err != nil {
return err
}
qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) {
assign := spec.Assign
values := spec.ScanValues
spec.ScanValues = func(columns []string) ([]any, error) {
values, err := values(columns[1:])
if err != nil {
return nil, err
}
return append([]any{new(sql.NullInt64)}, values...), nil
}
spec.Assign = func(columns []string, values []any) error {
outValue := int(values[0].(*sql.NullInt64).Int64)
inValue := int(values[1].(*sql.NullInt64).Int64)
if nids[inValue] == nil {
nids[inValue] = map[*Group]struct{}{byID[outValue]: {}}
return assign(columns[1:], values[1:])
}
nids[inValue][byID[outValue]] = struct{}{}
return nil
}
})
})
neighbors, err := withInterceptors[[]*StoragePolicy](ctx, query, qr, query.inters)
if err != nil {
return err
}
for _, n := range neighbors {
nodes, ok := nids[n.ID]
if !ok {
return fmt.Errorf(`unexpected "storage_policies_allowed" node returned %v`, n.ID)
}
for kn := range nodes {
assign(kn, n)
}
}
return nil
}
func (gq *GroupQuery) sqlCount(ctx context.Context) (int, error) {
_spec := gq.querySpec()

@ -198,6 +198,21 @@ func (gu *GroupUpdate) SetStoragePolicies(s *StoragePolicy) *GroupUpdate {
return gu.SetStoragePoliciesID(s.ID)
}
// AddStoragePoliciesAllowedIDs adds the "storage_policies_allowed" edge to the StoragePolicy entity by IDs.
func (gu *GroupUpdate) AddStoragePoliciesAllowedIDs(ids ...int) *GroupUpdate {
gu.mutation.AddStoragePoliciesAllowedIDs(ids...)
return gu
}
// AddStoragePoliciesAllowed adds the "storage_policies_allowed" edges to the StoragePolicy entity.
func (gu *GroupUpdate) AddStoragePoliciesAllowed(s ...*StoragePolicy) *GroupUpdate {
ids := make([]int, len(s))
for i := range s {
ids[i] = s[i].ID
}
return gu.AddStoragePoliciesAllowedIDs(ids...)
}
// Mutation returns the GroupMutation object of the builder.
func (gu *GroupUpdate) Mutation() *GroupMutation {
return gu.mutation
@ -230,6 +245,27 @@ func (gu *GroupUpdate) ClearStoragePolicies() *GroupUpdate {
return gu
}
// ClearStoragePoliciesAllowed clears all "storage_policies_allowed" edges to the StoragePolicy entity.
func (gu *GroupUpdate) ClearStoragePoliciesAllowed() *GroupUpdate {
gu.mutation.ClearStoragePoliciesAllowed()
return gu
}
// RemoveStoragePoliciesAllowedIDs removes the "storage_policies_allowed" edge to StoragePolicy entities by IDs.
func (gu *GroupUpdate) RemoveStoragePoliciesAllowedIDs(ids ...int) *GroupUpdate {
gu.mutation.RemoveStoragePoliciesAllowedIDs(ids...)
return gu
}
// RemoveStoragePoliciesAllowed removes "storage_policies_allowed" edges to StoragePolicy entities.
func (gu *GroupUpdate) RemoveStoragePoliciesAllowed(s ...*StoragePolicy) *GroupUpdate {
ids := make([]int, len(s))
for i := range s {
ids[i] = s[i].ID
}
return gu.RemoveStoragePoliciesAllowedIDs(ids...)
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (gu *GroupUpdate) Save(ctx context.Context) (int, error) {
if err := gu.defaults(); err != nil {
@ -394,6 +430,51 @@ func (gu *GroupUpdate) sqlSave(ctx context.Context) (n int, err error) {
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if gu.mutation.StoragePoliciesAllowedCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: false,
Table: group.StoragePoliciesAllowedTable,
Columns: group.StoragePoliciesAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(storagepolicy.FieldID, field.TypeInt),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := gu.mutation.RemovedStoragePoliciesAllowedIDs(); len(nodes) > 0 && !gu.mutation.StoragePoliciesAllowedCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: false,
Table: group.StoragePoliciesAllowedTable,
Columns: group.StoragePoliciesAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(storagepolicy.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := gu.mutation.StoragePoliciesAllowedIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: false,
Table: group.StoragePoliciesAllowedTable,
Columns: group.StoragePoliciesAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(storagepolicy.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if n, err = sqlgraph.UpdateNodes(ctx, gu.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{group.Label}
@ -580,6 +661,21 @@ func (guo *GroupUpdateOne) SetStoragePolicies(s *StoragePolicy) *GroupUpdateOne
return guo.SetStoragePoliciesID(s.ID)
}
// AddStoragePoliciesAllowedIDs adds the "storage_policies_allowed" edge to the StoragePolicy entity by IDs.
func (guo *GroupUpdateOne) AddStoragePoliciesAllowedIDs(ids ...int) *GroupUpdateOne {
guo.mutation.AddStoragePoliciesAllowedIDs(ids...)
return guo
}
// AddStoragePoliciesAllowed adds the "storage_policies_allowed" edges to the StoragePolicy entity.
func (guo *GroupUpdateOne) AddStoragePoliciesAllowed(s ...*StoragePolicy) *GroupUpdateOne {
ids := make([]int, len(s))
for i := range s {
ids[i] = s[i].ID
}
return guo.AddStoragePoliciesAllowedIDs(ids...)
}
// Mutation returns the GroupMutation object of the builder.
func (guo *GroupUpdateOne) Mutation() *GroupMutation {
return guo.mutation
@ -612,6 +708,27 @@ func (guo *GroupUpdateOne) ClearStoragePolicies() *GroupUpdateOne {
return guo
}
// ClearStoragePoliciesAllowed clears all "storage_policies_allowed" edges to the StoragePolicy entity.
func (guo *GroupUpdateOne) ClearStoragePoliciesAllowed() *GroupUpdateOne {
guo.mutation.ClearStoragePoliciesAllowed()
return guo
}
// RemoveStoragePoliciesAllowedIDs removes the "storage_policies_allowed" edge to StoragePolicy entities by IDs.
func (guo *GroupUpdateOne) RemoveStoragePoliciesAllowedIDs(ids ...int) *GroupUpdateOne {
guo.mutation.RemoveStoragePoliciesAllowedIDs(ids...)
return guo
}
// RemoveStoragePoliciesAllowed removes "storage_policies_allowed" edges to StoragePolicy entities.
func (guo *GroupUpdateOne) RemoveStoragePoliciesAllowed(s ...*StoragePolicy) *GroupUpdateOne {
ids := make([]int, len(s))
for i := range s {
ids[i] = s[i].ID
}
return guo.RemoveStoragePoliciesAllowedIDs(ids...)
}
// Where appends a list predicates to the GroupUpdate builder.
func (guo *GroupUpdateOne) Where(ps ...predicate.Group) *GroupUpdateOne {
guo.mutation.Where(ps...)
@ -806,6 +923,51 @@ func (guo *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if guo.mutation.StoragePoliciesAllowedCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: false,
Table: group.StoragePoliciesAllowedTable,
Columns: group.StoragePoliciesAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(storagepolicy.FieldID, field.TypeInt),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := guo.mutation.RemovedStoragePoliciesAllowedIDs(); len(nodes) > 0 && !guo.mutation.StoragePoliciesAllowedCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: false,
Table: group.StoragePoliciesAllowedTable,
Columns: group.StoragePoliciesAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(storagepolicy.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := guo.mutation.StoragePoliciesAllowedIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: false,
Table: group.StoragePoliciesAllowedTable,
Columns: group.StoragePoliciesAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(storagepolicy.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &Group{config: guo.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues

File diff suppressed because one or more lines are too long

@ -526,6 +526,31 @@ var (
},
},
}
// GroupStoragePoliciesAllowedColumns holds the columns for the "group_storage_policies_allowed" table.
GroupStoragePoliciesAllowedColumns = []*schema.Column{
{Name: "group_id", Type: field.TypeInt},
{Name: "storage_policy_id", Type: field.TypeInt},
}
// GroupStoragePoliciesAllowedTable holds the schema information for the "group_storage_policies_allowed" table.
GroupStoragePoliciesAllowedTable = &schema.Table{
Name: "group_storage_policies_allowed",
Columns: GroupStoragePoliciesAllowedColumns,
PrimaryKey: []*schema.Column{GroupStoragePoliciesAllowedColumns[0], GroupStoragePoliciesAllowedColumns[1]},
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "group_storage_policies_allowed_group_id",
Columns: []*schema.Column{GroupStoragePoliciesAllowedColumns[0]},
RefColumns: []*schema.Column{GroupsColumns[0]},
OnDelete: schema.Cascade,
},
{
Symbol: "group_storage_policies_allowed_storage_policy_id",
Columns: []*schema.Column{GroupStoragePoliciesAllowedColumns[1]},
RefColumns: []*schema.Column{StoragePoliciesColumns[0]},
OnDelete: schema.Cascade,
},
},
}
// Tables holds all the tables in the schema.
Tables = []*schema.Table{
DavAccountsTable,
@ -545,6 +570,7 @@ var (
TasksTable,
UsersTable,
FileEntitiesTable,
GroupStoragePoliciesAllowedTable,
}
)
@ -569,4 +595,6 @@ func init() {
UsersTable.ForeignKeys[0].RefTable = GroupsTable
FileEntitiesTable.ForeignKeys[0].RefTable = FilesTable
FileEntitiesTable.ForeignKeys[1].RefTable = EntitiesTable
GroupStoragePoliciesAllowedTable.ForeignKeys[0].RefTable = GroupsTable
GroupStoragePoliciesAllowedTable.ForeignKeys[1].RefTable = StoragePoliciesTable
}

@ -5317,28 +5317,31 @@ func (m *FsEventMutation) ResetEdge(name string) error {
// GroupMutation represents an operation that mutates the Group nodes in the graph.
type GroupMutation struct {
config
op Op
typ string
id *int
created_at *time.Time
updated_at *time.Time
deleted_at *time.Time
name *string
max_storage *int64
addmax_storage *int64
speed_limit *int
addspeed_limit *int
permissions **boolset.BooleanSet
settings **types.GroupSetting
clearedFields map[string]struct{}
users map[int]struct{}
removedusers map[int]struct{}
clearedusers bool
storage_policies *int
clearedstorage_policies bool
done bool
oldValue func(context.Context) (*Group, error)
predicates []predicate.Group
op Op
typ string
id *int
created_at *time.Time
updated_at *time.Time
deleted_at *time.Time
name *string
max_storage *int64
addmax_storage *int64
speed_limit *int
addspeed_limit *int
permissions **boolset.BooleanSet
settings **types.GroupSetting
clearedFields map[string]struct{}
users map[int]struct{}
removedusers map[int]struct{}
clearedusers bool
storage_policies *int
clearedstorage_policies bool
storage_policies_allowed map[int]struct{}
removedstorage_policies_allowed map[int]struct{}
clearedstorage_policies_allowed bool
done bool
oldValue func(context.Context) (*Group, error)
predicates []predicate.Group
}
var _ ent.Mutation = (*GroupMutation)(nil)
@ -5964,6 +5967,60 @@ func (m *GroupMutation) ResetStoragePolicies() {
m.clearedstorage_policies = false
}
// AddStoragePoliciesAllowedIDs adds the "storage_policies_allowed" edge to the StoragePolicy entity by ids.
func (m *GroupMutation) AddStoragePoliciesAllowedIDs(ids ...int) {
if m.storage_policies_allowed == nil {
m.storage_policies_allowed = make(map[int]struct{})
}
for i := range ids {
m.storage_policies_allowed[ids[i]] = struct{}{}
}
}
// ClearStoragePoliciesAllowed clears the "storage_policies_allowed" edge to the StoragePolicy entity.
func (m *GroupMutation) ClearStoragePoliciesAllowed() {
m.clearedstorage_policies_allowed = true
}
// StoragePoliciesAllowedCleared reports if the "storage_policies_allowed" edge to the StoragePolicy entity was cleared.
func (m *GroupMutation) StoragePoliciesAllowedCleared() bool {
return m.clearedstorage_policies_allowed
}
// RemoveStoragePoliciesAllowedIDs removes the "storage_policies_allowed" edge to the StoragePolicy entity by IDs.
func (m *GroupMutation) RemoveStoragePoliciesAllowedIDs(ids ...int) {
if m.removedstorage_policies_allowed == nil {
m.removedstorage_policies_allowed = make(map[int]struct{})
}
for i := range ids {
delete(m.storage_policies_allowed, ids[i])
m.removedstorage_policies_allowed[ids[i]] = struct{}{}
}
}
// RemovedStoragePoliciesAllowed returns the removed IDs of the "storage_policies_allowed" edge to the StoragePolicy entity.
func (m *GroupMutation) RemovedStoragePoliciesAllowedIDs() (ids []int) {
for id := range m.removedstorage_policies_allowed {
ids = append(ids, id)
}
return
}
// StoragePoliciesAllowedIDs returns the "storage_policies_allowed" edge IDs in the mutation.
func (m *GroupMutation) StoragePoliciesAllowedIDs() (ids []int) {
for id := range m.storage_policies_allowed {
ids = append(ids, id)
}
return
}
// ResetStoragePoliciesAllowed resets all changes to the "storage_policies_allowed" edge.
func (m *GroupMutation) ResetStoragePoliciesAllowed() {
m.storage_policies_allowed = nil
m.clearedstorage_policies_allowed = false
m.removedstorage_policies_allowed = nil
}
// Where appends a list predicates to the GroupMutation builder.
func (m *GroupMutation) Where(ps ...predicate.Group) {
m.predicates = append(m.predicates, ps...)
@ -6293,13 +6350,16 @@ func (m *GroupMutation) ResetField(name string) error {
// AddedEdges returns all edge names that were set/added in this mutation.
func (m *GroupMutation) AddedEdges() []string {
edges := make([]string, 0, 2)
edges := make([]string, 0, 3)
if m.users != nil {
edges = append(edges, group.EdgeUsers)
}
if m.storage_policies != nil {
edges = append(edges, group.EdgeStoragePolicies)
}
if m.storage_policies_allowed != nil {
edges = append(edges, group.EdgeStoragePoliciesAllowed)
}
return edges
}
@ -6317,16 +6377,25 @@ func (m *GroupMutation) AddedIDs(name string) []ent.Value {
if id := m.storage_policies; id != nil {
return []ent.Value{*id}
}
case group.EdgeStoragePoliciesAllowed:
ids := make([]ent.Value, 0, len(m.storage_policies_allowed))
for id := range m.storage_policies_allowed {
ids = append(ids, id)
}
return ids
}
return nil
}
// RemovedEdges returns all edge names that were removed in this mutation.
func (m *GroupMutation) RemovedEdges() []string {
edges := make([]string, 0, 2)
edges := make([]string, 0, 3)
if m.removedusers != nil {
edges = append(edges, group.EdgeUsers)
}
if m.removedstorage_policies_allowed != nil {
edges = append(edges, group.EdgeStoragePoliciesAllowed)
}
return edges
}
@ -6340,19 +6409,28 @@ func (m *GroupMutation) RemovedIDs(name string) []ent.Value {
ids = append(ids, id)
}
return ids
case group.EdgeStoragePoliciesAllowed:
ids := make([]ent.Value, 0, len(m.removedstorage_policies_allowed))
for id := range m.removedstorage_policies_allowed {
ids = append(ids, id)
}
return ids
}
return nil
}
// ClearedEdges returns all edge names that were cleared in this mutation.
func (m *GroupMutation) ClearedEdges() []string {
edges := make([]string, 0, 2)
edges := make([]string, 0, 3)
if m.clearedusers {
edges = append(edges, group.EdgeUsers)
}
if m.clearedstorage_policies {
edges = append(edges, group.EdgeStoragePolicies)
}
if m.clearedstorage_policies_allowed {
edges = append(edges, group.EdgeStoragePoliciesAllowed)
}
return edges
}
@ -6364,6 +6442,8 @@ func (m *GroupMutation) EdgeCleared(name string) bool {
return m.clearedusers
case group.EdgeStoragePolicies:
return m.clearedstorage_policies
case group.EdgeStoragePoliciesAllowed:
return m.clearedstorage_policies_allowed
}
return false
}
@ -6389,6 +6469,9 @@ func (m *GroupMutation) ResetEdge(name string) error {
case group.EdgeStoragePolicies:
m.ResetStoragePolicies()
return nil
case group.EdgeStoragePoliciesAllowed:
m.ResetStoragePoliciesAllowed()
return nil
}
return fmt.Errorf("unknown Group edge %s", name)
}
@ -12511,39 +12594,42 @@ func (m *ShareMutation) ResetEdge(name string) error {
// StoragePolicyMutation represents an operation that mutates the StoragePolicy nodes in the graph.
type StoragePolicyMutation struct {
config
op Op
typ string
id *int
created_at *time.Time
updated_at *time.Time
deleted_at *time.Time
name *string
_type *string
server *string
bucket_name *string
is_private *bool
access_key *string
secret_key *string
max_size *int64
addmax_size *int64
dir_name_rule *string
file_name_rule *string
settings **types.PolicySetting
clearedFields map[string]struct{}
groups map[int]struct{}
removedgroups map[int]struct{}
clearedgroups bool
files map[int]struct{}
removedfiles map[int]struct{}
clearedfiles bool
entities map[int]struct{}
removedentities map[int]struct{}
clearedentities bool
node *int
clearednode bool
done bool
oldValue func(context.Context) (*StoragePolicy, error)
predicates []predicate.StoragePolicy
op Op
typ string
id *int
created_at *time.Time
updated_at *time.Time
deleted_at *time.Time
name *string
_type *string
server *string
bucket_name *string
is_private *bool
access_key *string
secret_key *string
max_size *int64
addmax_size *int64
dir_name_rule *string
file_name_rule *string
settings **types.PolicySetting
clearedFields map[string]struct{}
groups map[int]struct{}
removedgroups map[int]struct{}
clearedgroups bool
files map[int]struct{}
removedfiles map[int]struct{}
clearedfiles bool
entities map[int]struct{}
removedentities map[int]struct{}
clearedentities bool
node *int
clearednode bool
groups_allowed map[int]struct{}
removedgroups_allowed map[int]struct{}
clearedgroups_allowed bool
done bool
oldValue func(context.Context) (*StoragePolicy, error)
predicates []predicate.StoragePolicy
}
var _ ent.Mutation = (*StoragePolicyMutation)(nil)
@ -13537,6 +13623,60 @@ func (m *StoragePolicyMutation) ResetNode() {
m.clearednode = false
}
// AddGroupsAllowedIDs adds the "groups_allowed" edge to the Group entity by ids.
func (m *StoragePolicyMutation) AddGroupsAllowedIDs(ids ...int) {
if m.groups_allowed == nil {
m.groups_allowed = make(map[int]struct{})
}
for i := range ids {
m.groups_allowed[ids[i]] = struct{}{}
}
}
// ClearGroupsAllowed clears the "groups_allowed" edge to the Group entity.
func (m *StoragePolicyMutation) ClearGroupsAllowed() {
m.clearedgroups_allowed = true
}
// GroupsAllowedCleared reports if the "groups_allowed" edge to the Group entity was cleared.
func (m *StoragePolicyMutation) GroupsAllowedCleared() bool {
return m.clearedgroups_allowed
}
// RemoveGroupsAllowedIDs removes the "groups_allowed" edge to the Group entity by IDs.
func (m *StoragePolicyMutation) RemoveGroupsAllowedIDs(ids ...int) {
if m.removedgroups_allowed == nil {
m.removedgroups_allowed = make(map[int]struct{})
}
for i := range ids {
delete(m.groups_allowed, ids[i])
m.removedgroups_allowed[ids[i]] = struct{}{}
}
}
// RemovedGroupsAllowed returns the removed IDs of the "groups_allowed" edge to the Group entity.
func (m *StoragePolicyMutation) RemovedGroupsAllowedIDs() (ids []int) {
for id := range m.removedgroups_allowed {
ids = append(ids, id)
}
return
}
// GroupsAllowedIDs returns the "groups_allowed" edge IDs in the mutation.
func (m *StoragePolicyMutation) GroupsAllowedIDs() (ids []int) {
for id := range m.groups_allowed {
ids = append(ids, id)
}
return
}
// ResetGroupsAllowed resets all changes to the "groups_allowed" edge.
func (m *StoragePolicyMutation) ResetGroupsAllowed() {
m.groups_allowed = nil
m.clearedgroups_allowed = false
m.removedgroups_allowed = nil
}
// Where appends a list predicates to the StoragePolicyMutation builder.
func (m *StoragePolicyMutation) Where(ps ...predicate.StoragePolicy) {
m.predicates = append(m.predicates, ps...)
@ -13992,7 +14132,7 @@ func (m *StoragePolicyMutation) ResetField(name string) error {
// AddedEdges returns all edge names that were set/added in this mutation.
func (m *StoragePolicyMutation) AddedEdges() []string {
edges := make([]string, 0, 4)
edges := make([]string, 0, 5)
if m.groups != nil {
edges = append(edges, storagepolicy.EdgeGroups)
}
@ -14005,6 +14145,9 @@ func (m *StoragePolicyMutation) AddedEdges() []string {
if m.node != nil {
edges = append(edges, storagepolicy.EdgeNode)
}
if m.groups_allowed != nil {
edges = append(edges, storagepolicy.EdgeGroupsAllowed)
}
return edges
}
@ -14034,13 +14177,19 @@ func (m *StoragePolicyMutation) AddedIDs(name string) []ent.Value {
if id := m.node; id != nil {
return []ent.Value{*id}
}
case storagepolicy.EdgeGroupsAllowed:
ids := make([]ent.Value, 0, len(m.groups_allowed))
for id := range m.groups_allowed {
ids = append(ids, id)
}
return ids
}
return nil
}
// RemovedEdges returns all edge names that were removed in this mutation.
func (m *StoragePolicyMutation) RemovedEdges() []string {
edges := make([]string, 0, 4)
edges := make([]string, 0, 5)
if m.removedgroups != nil {
edges = append(edges, storagepolicy.EdgeGroups)
}
@ -14050,6 +14199,9 @@ func (m *StoragePolicyMutation) RemovedEdges() []string {
if m.removedentities != nil {
edges = append(edges, storagepolicy.EdgeEntities)
}
if m.removedgroups_allowed != nil {
edges = append(edges, storagepolicy.EdgeGroupsAllowed)
}
return edges
}
@ -14075,13 +14227,19 @@ func (m *StoragePolicyMutation) RemovedIDs(name string) []ent.Value {
ids = append(ids, id)
}
return ids
case storagepolicy.EdgeGroupsAllowed:
ids := make([]ent.Value, 0, len(m.removedgroups_allowed))
for id := range m.removedgroups_allowed {
ids = append(ids, id)
}
return ids
}
return nil
}
// ClearedEdges returns all edge names that were cleared in this mutation.
func (m *StoragePolicyMutation) ClearedEdges() []string {
edges := make([]string, 0, 4)
edges := make([]string, 0, 5)
if m.clearedgroups {
edges = append(edges, storagepolicy.EdgeGroups)
}
@ -14094,6 +14252,9 @@ func (m *StoragePolicyMutation) ClearedEdges() []string {
if m.clearednode {
edges = append(edges, storagepolicy.EdgeNode)
}
if m.clearedgroups_allowed {
edges = append(edges, storagepolicy.EdgeGroupsAllowed)
}
return edges
}
@ -14109,6 +14270,8 @@ func (m *StoragePolicyMutation) EdgeCleared(name string) bool {
return m.clearedentities
case storagepolicy.EdgeNode:
return m.clearednode
case storagepolicy.EdgeGroupsAllowed:
return m.clearedgroups_allowed
}
return false
}
@ -14140,6 +14303,9 @@ func (m *StoragePolicyMutation) ResetEdge(name string) error {
case storagepolicy.EdgeNode:
m.ResetNode()
return nil
case storagepolicy.EdgeGroupsAllowed:
m.ResetGroupsAllowed()
return nil
}
return fmt.Errorf("unknown StoragePolicy edge %s", name)
}

@ -41,5 +41,9 @@ func (Group) Edges() []ent.Edge {
Ref("groups").
Field("storage_policy_id").
Unique(),
// storage_policies_allowed is the set of storage policies a member of this
// group may upload to. storage_policy_id above remains the default policy
// used when the client does not pick one. Many-to-many.
edge.To("storage_policies_allowed", StoragePolicy.Type),
}
}

@ -54,5 +54,9 @@ func (StoragePolicy) Edges() []ent.Edge {
Ref("storage_policy").
Field("node_id").
Unique(),
// groups_allowed is the inverse of Group.storage_policies_allowed: the set
// of groups that are permitted to upload to this policy.
edge.From("groups_allowed", Group.Type).
Ref("storage_policies_allowed"),
}
}

@ -66,9 +66,11 @@ type StoragePolicyEdges struct {
Entities []*Entity `json:"entities,omitempty"`
// Node holds the value of the node edge.
Node *Node `json:"node,omitempty"`
// GroupsAllowed holds the value of the groups_allowed edge.
GroupsAllowed []*Group `json:"groups_allowed,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
loadedTypes [4]bool
loadedTypes [5]bool
}
// GroupsOrErr returns the Groups value or an error if the edge
@ -111,6 +113,15 @@ func (e StoragePolicyEdges) NodeOrErr() (*Node, error) {
return nil, &NotLoadedError{edge: "node"}
}
// GroupsAllowedOrErr returns the GroupsAllowed value or an error if the edge
// was not loaded in eager-loading.
func (e StoragePolicyEdges) GroupsAllowedOrErr() ([]*Group, error) {
if e.loadedTypes[4] {
return e.GroupsAllowed, nil
}
return nil, &NotLoadedError{edge: "groups_allowed"}
}
// scanValues returns the types for scanning values from sql.Rows.
func (*StoragePolicy) scanValues(columns []string) ([]any, error) {
values := make([]any, len(columns))
@ -273,6 +284,11 @@ func (sp *StoragePolicy) QueryNode() *NodeQuery {
return NewStoragePolicyClient(sp.config).QueryNode(sp)
}
// QueryGroupsAllowed queries the "groups_allowed" edge of the StoragePolicy entity.
func (sp *StoragePolicy) QueryGroupsAllowed() *GroupQuery {
return NewStoragePolicyClient(sp.config).QueryGroupsAllowed(sp)
}
// Update returns a builder for updating this StoragePolicy.
// Note that you need to call StoragePolicy.Unwrap() before calling this method if this StoragePolicy
// was returned from a transaction, and the transaction was committed or rolled back.
@ -370,5 +386,11 @@ func (e *StoragePolicy) SetNode(v *Node) {
e.Edges.loadedTypes[3] = true
}
// SetGroupsAllowed manually set the edge as loaded state.
func (e *StoragePolicy) SetGroupsAllowed(v []*Group) {
e.Edges.GroupsAllowed = v
e.Edges.loadedTypes[4] = true
}
// StoragePolicies is a parsable slice of StoragePolicy.
type StoragePolicies []*StoragePolicy

@ -54,6 +54,8 @@ const (
EdgeEntities = "entities"
// EdgeNode holds the string denoting the node edge name in mutations.
EdgeNode = "node"
// EdgeGroupsAllowed holds the string denoting the groups_allowed edge name in mutations.
EdgeGroupsAllowed = "groups_allowed"
// Table holds the table name of the storagepolicy in the database.
Table = "storage_policies"
// GroupsTable is the table that holds the groups relation/edge.
@ -84,6 +86,11 @@ const (
NodeInverseTable = "nodes"
// NodeColumn is the table column denoting the node relation/edge.
NodeColumn = "node_id"
// GroupsAllowedTable is the table that holds the groups_allowed relation/edge. The primary key declared below.
GroupsAllowedTable = "group_storage_policies_allowed"
// GroupsAllowedInverseTable is the table name for the Group entity.
// It exists in this package in order to avoid circular dependency with the "group" package.
GroupsAllowedInverseTable = "groups"
)
// Columns holds all SQL columns for storagepolicy fields.
@ -106,6 +113,12 @@ var Columns = []string{
FieldNodeID,
}
var (
// GroupsAllowedPrimaryKey and GroupsAllowedColumn2 are the table columns denoting the
// primary key for the groups_allowed relation (M2M).
GroupsAllowedPrimaryKey = []string{"group_id", "storage_policy_id"}
)
// ValidColumn reports if the column name is valid (part of the table columns).
func ValidColumn(column string) bool {
for i := range Columns {
@ -260,6 +273,20 @@ func ByNodeField(field string, opts ...sql.OrderTermOption) OrderOption {
sqlgraph.OrderByNeighborTerms(s, newNodeStep(), sql.OrderByField(field, opts...))
}
}
// ByGroupsAllowedCount orders the results by groups_allowed count.
func ByGroupsAllowedCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newGroupsAllowedStep(), opts...)
}
}
// ByGroupsAllowed orders the results by groups_allowed terms.
func ByGroupsAllowed(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newGroupsAllowedStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
func newGroupsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
@ -288,3 +315,10 @@ func newNodeStep() *sqlgraph.Step {
sqlgraph.Edge(sqlgraph.M2O, true, NodeTable, NodeColumn),
)
}
func newGroupsAllowedStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(GroupsAllowedInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2M, true, GroupsAllowedTable, GroupsAllowedPrimaryKey...),
)
}

@ -1037,6 +1037,29 @@ func HasNodeWith(preds ...predicate.Node) predicate.StoragePolicy {
})
}
// HasGroupsAllowed applies the HasEdge predicate on the "groups_allowed" edge.
func HasGroupsAllowed() predicate.StoragePolicy {
return predicate.StoragePolicy(func(s *sql.Selector) {
step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.Edge(sqlgraph.M2M, true, GroupsAllowedTable, GroupsAllowedPrimaryKey...),
)
sqlgraph.HasNeighbors(s, step)
})
}
// HasGroupsAllowedWith applies the HasEdge predicate on the "groups_allowed" edge with a given conditions (other predicates).
func HasGroupsAllowedWith(preds ...predicate.Group) predicate.StoragePolicy {
return predicate.StoragePolicy(func(s *sql.Selector) {
step := newGroupsAllowedStep()
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds {
p(s)
}
})
})
}
// And groups predicates with the AND operator between them.
func And(predicates ...predicate.StoragePolicy) predicate.StoragePolicy {
return predicate.StoragePolicy(sql.AndPredicates(predicates...))

@ -263,6 +263,21 @@ func (spc *StoragePolicyCreate) SetNode(n *Node) *StoragePolicyCreate {
return spc.SetNodeID(n.ID)
}
// AddGroupsAllowedIDs adds the "groups_allowed" edge to the Group entity by IDs.
func (spc *StoragePolicyCreate) AddGroupsAllowedIDs(ids ...int) *StoragePolicyCreate {
spc.mutation.AddGroupsAllowedIDs(ids...)
return spc
}
// AddGroupsAllowed adds the "groups_allowed" edges to the Group entity.
func (spc *StoragePolicyCreate) AddGroupsAllowed(g ...*Group) *StoragePolicyCreate {
ids := make([]int, len(g))
for i := range g {
ids[i] = g[i].ID
}
return spc.AddGroupsAllowedIDs(ids...)
}
// Mutation returns the StoragePolicyMutation object of the builder.
func (spc *StoragePolicyCreate) Mutation() *StoragePolicyMutation {
return spc.mutation
@ -490,6 +505,22 @@ func (spc *StoragePolicyCreate) createSpec() (*StoragePolicy, *sqlgraph.CreateSp
_node.NodeID = nodes[0]
_spec.Edges = append(_spec.Edges, edge)
}
if nodes := spc.mutation.GroupsAllowedIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: true,
Table: storagepolicy.GroupsAllowedTable,
Columns: storagepolicy.GroupsAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges = append(_spec.Edges, edge)
}
return _node, _spec
}

@ -22,14 +22,15 @@ import (
// StoragePolicyQuery is the builder for querying StoragePolicy entities.
type StoragePolicyQuery struct {
config
ctx *QueryContext
order []storagepolicy.OrderOption
inters []Interceptor
predicates []predicate.StoragePolicy
withGroups *GroupQuery
withFiles *FileQuery
withEntities *EntityQuery
withNode *NodeQuery
ctx *QueryContext
order []storagepolicy.OrderOption
inters []Interceptor
predicates []predicate.StoragePolicy
withGroups *GroupQuery
withFiles *FileQuery
withEntities *EntityQuery
withNode *NodeQuery
withGroupsAllowed *GroupQuery
// intermediate query (i.e. traversal path).
sql *sql.Selector
path func(context.Context) (*sql.Selector, error)
@ -154,6 +155,28 @@ func (spq *StoragePolicyQuery) QueryNode() *NodeQuery {
return query
}
// QueryGroupsAllowed chains the current query on the "groups_allowed" edge.
func (spq *StoragePolicyQuery) QueryGroupsAllowed() *GroupQuery {
query := (&GroupClient{config: spq.config}).Query()
query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
if err := spq.prepareQuery(ctx); err != nil {
return nil, err
}
selector := spq.sqlQuery(ctx)
if err := selector.Err(); err != nil {
return nil, err
}
step := sqlgraph.NewStep(
sqlgraph.From(storagepolicy.Table, storagepolicy.FieldID, selector),
sqlgraph.To(group.Table, group.FieldID),
sqlgraph.Edge(sqlgraph.M2M, true, storagepolicy.GroupsAllowedTable, storagepolicy.GroupsAllowedPrimaryKey...),
)
fromU = sqlgraph.SetNeighbors(spq.driver.Dialect(), step)
return fromU, nil
}
return query
}
// First returns the first StoragePolicy entity from the query.
// Returns a *NotFoundError when no StoragePolicy was found.
func (spq *StoragePolicyQuery) First(ctx context.Context) (*StoragePolicy, error) {
@ -341,15 +364,16 @@ func (spq *StoragePolicyQuery) Clone() *StoragePolicyQuery {
return nil
}
return &StoragePolicyQuery{
config: spq.config,
ctx: spq.ctx.Clone(),
order: append([]storagepolicy.OrderOption{}, spq.order...),
inters: append([]Interceptor{}, spq.inters...),
predicates: append([]predicate.StoragePolicy{}, spq.predicates...),
withGroups: spq.withGroups.Clone(),
withFiles: spq.withFiles.Clone(),
withEntities: spq.withEntities.Clone(),
withNode: spq.withNode.Clone(),
config: spq.config,
ctx: spq.ctx.Clone(),
order: append([]storagepolicy.OrderOption{}, spq.order...),
inters: append([]Interceptor{}, spq.inters...),
predicates: append([]predicate.StoragePolicy{}, spq.predicates...),
withGroups: spq.withGroups.Clone(),
withFiles: spq.withFiles.Clone(),
withEntities: spq.withEntities.Clone(),
withNode: spq.withNode.Clone(),
withGroupsAllowed: spq.withGroupsAllowed.Clone(),
// clone intermediate query.
sql: spq.sql.Clone(),
path: spq.path,
@ -400,6 +424,17 @@ func (spq *StoragePolicyQuery) WithNode(opts ...func(*NodeQuery)) *StoragePolicy
return spq
}
// WithGroupsAllowed tells the query-builder to eager-load the nodes that are connected to
// the "groups_allowed" edge. The optional arguments are used to configure the query builder of the edge.
func (spq *StoragePolicyQuery) WithGroupsAllowed(opts ...func(*GroupQuery)) *StoragePolicyQuery {
query := (&GroupClient{config: spq.config}).Query()
for _, opt := range opts {
opt(query)
}
spq.withGroupsAllowed = query
return spq
}
// GroupBy is used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
@ -478,11 +513,12 @@ func (spq *StoragePolicyQuery) sqlAll(ctx context.Context, hooks ...queryHook) (
var (
nodes = []*StoragePolicy{}
_spec = spq.querySpec()
loadedTypes = [4]bool{
loadedTypes = [5]bool{
spq.withGroups != nil,
spq.withFiles != nil,
spq.withEntities != nil,
spq.withNode != nil,
spq.withGroupsAllowed != nil,
}
)
_spec.ScanValues = func(columns []string) ([]any, error) {
@ -530,6 +566,13 @@ func (spq *StoragePolicyQuery) sqlAll(ctx context.Context, hooks ...queryHook) (
return nil, err
}
}
if query := spq.withGroupsAllowed; query != nil {
if err := spq.loadGroupsAllowed(ctx, query, nodes,
func(n *StoragePolicy) { n.Edges.GroupsAllowed = []*Group{} },
func(n *StoragePolicy, e *Group) { n.Edges.GroupsAllowed = append(n.Edges.GroupsAllowed, e) }); err != nil {
return nil, err
}
}
return nodes, nil
}
@ -652,6 +695,67 @@ func (spq *StoragePolicyQuery) loadNode(ctx context.Context, query *NodeQuery, n
}
return nil
}
func (spq *StoragePolicyQuery) loadGroupsAllowed(ctx context.Context, query *GroupQuery, nodes []*StoragePolicy, init func(*StoragePolicy), assign func(*StoragePolicy, *Group)) error {
edgeIDs := make([]driver.Value, len(nodes))
byID := make(map[int]*StoragePolicy)
nids := make(map[int]map[*StoragePolicy]struct{})
for i, node := range nodes {
edgeIDs[i] = node.ID
byID[node.ID] = node
if init != nil {
init(node)
}
}
query.Where(func(s *sql.Selector) {
joinT := sql.Table(storagepolicy.GroupsAllowedTable)
s.Join(joinT).On(s.C(group.FieldID), joinT.C(storagepolicy.GroupsAllowedPrimaryKey[0]))
s.Where(sql.InValues(joinT.C(storagepolicy.GroupsAllowedPrimaryKey[1]), edgeIDs...))
columns := s.SelectedColumns()
s.Select(joinT.C(storagepolicy.GroupsAllowedPrimaryKey[1]))
s.AppendSelect(columns...)
s.SetDistinct(false)
})
if err := query.prepareQuery(ctx); err != nil {
return err
}
qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) {
return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) {
assign := spec.Assign
values := spec.ScanValues
spec.ScanValues = func(columns []string) ([]any, error) {
values, err := values(columns[1:])
if err != nil {
return nil, err
}
return append([]any{new(sql.NullInt64)}, values...), nil
}
spec.Assign = func(columns []string, values []any) error {
outValue := int(values[0].(*sql.NullInt64).Int64)
inValue := int(values[1].(*sql.NullInt64).Int64)
if nids[inValue] == nil {
nids[inValue] = map[*StoragePolicy]struct{}{byID[outValue]: {}}
return assign(columns[1:], values[1:])
}
nids[inValue][byID[outValue]] = struct{}{}
return nil
}
})
})
neighbors, err := withInterceptors[[]*Group](ctx, query, qr, query.inters)
if err != nil {
return err
}
for _, n := range neighbors {
nodes, ok := nids[n.ID]
if !ok {
return fmt.Errorf(`unexpected "groups_allowed" node returned %v`, n.ID)
}
for kn := range nodes {
assign(kn, n)
}
}
return nil
}
func (spq *StoragePolicyQuery) sqlCount(ctx context.Context) (int, error) {
_spec := spq.querySpec()

@ -336,6 +336,21 @@ func (spu *StoragePolicyUpdate) SetNode(n *Node) *StoragePolicyUpdate {
return spu.SetNodeID(n.ID)
}
// AddGroupsAllowedIDs adds the "groups_allowed" edge to the Group entity by IDs.
func (spu *StoragePolicyUpdate) AddGroupsAllowedIDs(ids ...int) *StoragePolicyUpdate {
spu.mutation.AddGroupsAllowedIDs(ids...)
return spu
}
// AddGroupsAllowed adds the "groups_allowed" edges to the Group entity.
func (spu *StoragePolicyUpdate) AddGroupsAllowed(g ...*Group) *StoragePolicyUpdate {
ids := make([]int, len(g))
for i := range g {
ids[i] = g[i].ID
}
return spu.AddGroupsAllowedIDs(ids...)
}
// Mutation returns the StoragePolicyMutation object of the builder.
func (spu *StoragePolicyUpdate) Mutation() *StoragePolicyMutation {
return spu.mutation
@ -410,6 +425,27 @@ func (spu *StoragePolicyUpdate) ClearNode() *StoragePolicyUpdate {
return spu
}
// ClearGroupsAllowed clears all "groups_allowed" edges to the Group entity.
func (spu *StoragePolicyUpdate) ClearGroupsAllowed() *StoragePolicyUpdate {
spu.mutation.ClearGroupsAllowed()
return spu
}
// RemoveGroupsAllowedIDs removes the "groups_allowed" edge to Group entities by IDs.
func (spu *StoragePolicyUpdate) RemoveGroupsAllowedIDs(ids ...int) *StoragePolicyUpdate {
spu.mutation.RemoveGroupsAllowedIDs(ids...)
return spu
}
// RemoveGroupsAllowed removes "groups_allowed" edges to Group entities.
func (spu *StoragePolicyUpdate) RemoveGroupsAllowed(g ...*Group) *StoragePolicyUpdate {
ids := make([]int, len(g))
for i := range g {
ids[i] = g[i].ID
}
return spu.RemoveGroupsAllowedIDs(ids...)
}
// Save executes the query and returns the number of nodes affected by the update operation.
func (spu *StoragePolicyUpdate) Save(ctx context.Context) (int, error) {
if err := spu.defaults(); err != nil {
@ -697,6 +733,51 @@ func (spu *StoragePolicyUpdate) sqlSave(ctx context.Context) (n int, err error)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if spu.mutation.GroupsAllowedCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: true,
Table: storagepolicy.GroupsAllowedTable,
Columns: storagepolicy.GroupsAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := spu.mutation.RemovedGroupsAllowedIDs(); len(nodes) > 0 && !spu.mutation.GroupsAllowedCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: true,
Table: storagepolicy.GroupsAllowedTable,
Columns: storagepolicy.GroupsAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := spu.mutation.GroupsAllowedIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: true,
Table: storagepolicy.GroupsAllowedTable,
Columns: storagepolicy.GroupsAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if n, err = sqlgraph.UpdateNodes(ctx, spu.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{storagepolicy.Label}
@ -1020,6 +1101,21 @@ func (spuo *StoragePolicyUpdateOne) SetNode(n *Node) *StoragePolicyUpdateOne {
return spuo.SetNodeID(n.ID)
}
// AddGroupsAllowedIDs adds the "groups_allowed" edge to the Group entity by IDs.
func (spuo *StoragePolicyUpdateOne) AddGroupsAllowedIDs(ids ...int) *StoragePolicyUpdateOne {
spuo.mutation.AddGroupsAllowedIDs(ids...)
return spuo
}
// AddGroupsAllowed adds the "groups_allowed" edges to the Group entity.
func (spuo *StoragePolicyUpdateOne) AddGroupsAllowed(g ...*Group) *StoragePolicyUpdateOne {
ids := make([]int, len(g))
for i := range g {
ids[i] = g[i].ID
}
return spuo.AddGroupsAllowedIDs(ids...)
}
// Mutation returns the StoragePolicyMutation object of the builder.
func (spuo *StoragePolicyUpdateOne) Mutation() *StoragePolicyMutation {
return spuo.mutation
@ -1094,6 +1190,27 @@ func (spuo *StoragePolicyUpdateOne) ClearNode() *StoragePolicyUpdateOne {
return spuo
}
// ClearGroupsAllowed clears all "groups_allowed" edges to the Group entity.
func (spuo *StoragePolicyUpdateOne) ClearGroupsAllowed() *StoragePolicyUpdateOne {
spuo.mutation.ClearGroupsAllowed()
return spuo
}
// RemoveGroupsAllowedIDs removes the "groups_allowed" edge to Group entities by IDs.
func (spuo *StoragePolicyUpdateOne) RemoveGroupsAllowedIDs(ids ...int) *StoragePolicyUpdateOne {
spuo.mutation.RemoveGroupsAllowedIDs(ids...)
return spuo
}
// RemoveGroupsAllowed removes "groups_allowed" edges to Group entities.
func (spuo *StoragePolicyUpdateOne) RemoveGroupsAllowed(g ...*Group) *StoragePolicyUpdateOne {
ids := make([]int, len(g))
for i := range g {
ids[i] = g[i].ID
}
return spuo.RemoveGroupsAllowedIDs(ids...)
}
// Where appends a list predicates to the StoragePolicyUpdate builder.
func (spuo *StoragePolicyUpdateOne) Where(ps ...predicate.StoragePolicy) *StoragePolicyUpdateOne {
spuo.mutation.Where(ps...)
@ -1411,6 +1528,51 @@ func (spuo *StoragePolicyUpdateOne) sqlSave(ctx context.Context) (_node *Storage
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
if spuo.mutation.GroupsAllowedCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: true,
Table: storagepolicy.GroupsAllowedTable,
Columns: storagepolicy.GroupsAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt),
},
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := spuo.mutation.RemovedGroupsAllowedIDs(); len(nodes) > 0 && !spuo.mutation.GroupsAllowedCleared() {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: true,
Table: storagepolicy.GroupsAllowedTable,
Columns: storagepolicy.GroupsAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Clear = append(_spec.Edges.Clear, edge)
}
if nodes := spuo.mutation.GroupsAllowedIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2M,
Inverse: true,
Table: storagepolicy.GroupsAllowedTable,
Columns: storagepolicy.GroupsAllowedPrimaryKey,
Bidi: false,
Target: &sqlgraph.EdgeTarget{
IDSpec: sqlgraph.NewFieldSpec(group.FieldID, field.TypeInt),
},
}
for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k)
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
_node = &StoragePolicy{config: spuo.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues

@ -77,6 +77,12 @@ func (c *groupClient) ListAll(ctx context.Context) ([]*ent.Group, error) {
}
func (c *groupClient) Upsert(ctx context.Context, group *ent.Group) (*ent.Group, error) {
defaultPolicyID := 0
if group.Edges.StoragePolicies != nil && group.Edges.StoragePolicies.ID > 0 {
defaultPolicyID = group.Edges.StoragePolicies.ID
}
allowedIDs := allowedPolicyIDs(group, defaultPolicyID)
if group.ID == 0 {
stm := c.client.Group.Create().
SetName(group.Name).
@ -85,8 +91,11 @@ func (c *groupClient) Upsert(ctx context.Context, group *ent.Group) (*ent.Group,
SetPermissions(group.Permissions).
SetSettings(group.Settings)
if group.Edges.StoragePolicies != nil && group.Edges.StoragePolicies.ID > 0 {
stm.SetStoragePolicyID(group.Edges.StoragePolicies.ID)
if defaultPolicyID > 0 {
stm.SetStoragePolicyID(defaultPolicyID)
}
if len(allowedIDs) > 0 {
stm.AddStoragePoliciesAllowedIDs(allowedIDs...)
}
return stm.Save(ctx)
@ -98,10 +107,14 @@ func (c *groupClient) Upsert(ctx context.Context, group *ent.Group) (*ent.Group,
SetSpeedLimit(group.SpeedLimit).
SetPermissions(group.Permissions).
SetSettings(group.Settings).
ClearStoragePolicies()
ClearStoragePolicies().
ClearStoragePoliciesAllowed()
if group.Edges.StoragePolicies != nil && group.Edges.StoragePolicies.ID > 0 {
stm.SetStoragePolicyID(group.Edges.StoragePolicies.ID)
if defaultPolicyID > 0 {
stm.SetStoragePolicyID(defaultPolicyID)
}
if len(allowedIDs) > 0 {
stm.AddStoragePoliciesAllowedIDs(allowedIDs...)
}
res, err := stm.Save(ctx)
@ -112,6 +125,33 @@ func (c *groupClient) Upsert(ctx context.Context, group *ent.Group) (*ent.Group,
return res, nil
}
// allowedPolicyIDs computes the deduplicated set of storage policy ids a group is
// allowed to upload to, always including the default policy id (when set) so the
// default is guaranteed to be a member of the allowed set.
func allowedPolicyIDs(group *ent.Group, defaultPolicyID int) []int {
seen := make(map[int]struct{})
ids := make([]int, 0)
add := func(id int) {
if id <= 0 {
return
}
if _, ok := seen[id]; ok {
return
}
seen[id] = struct{}{}
ids = append(ids, id)
}
add(defaultPolicyID)
for _, p := range group.Edges.StoragePoliciesAllowed {
if p != nil {
add(p.ID)
}
}
return ids
}
func (c *groupClient) Delete(ctx context.Context, id int) error {
if err := c.client.Group.DeleteOneID(id).Exec(ctx); err != nil {
return fmt.Errorf("failed to delete group: %w", err)
@ -172,6 +212,9 @@ func withGroupEagerLoading(ctx context.Context, q *ent.GroupQuery) *ent.GroupQue
q.WithStoragePolicies(func(spq *ent.StoragePolicyQuery) {
withStoragePolicyEagerLoading(ctx, spq)
})
q.WithStoragePoliciesAllowed(func(spq *ent.StoragePolicyQuery) {
withStoragePolicyEagerLoading(ctx, spq)
})
}
return q
}

@ -47,6 +47,10 @@ func migrate(l logging.Logger, client *ent.Client, ctx context.Context, kv cache
return fmt.Errorf("failed migrating default storage policy: %w", err)
}
if err := migrateGroupAvailablePolicies(l, client, ctx); err != nil {
return fmt.Errorf("failed migrating group available storage policies: %w", err)
}
if err := migrateOAuthClient(l, client, ctx); err != nil {
return fmt.Errorf("failed migrating OAuth client: %w", err)
}
@ -135,6 +139,41 @@ func migrateSysGroups(l logging.Logger, client *ent.Client, ctx context.Context)
return nil
}
// migrateGroupAvailablePolicies backfills each group's allowed storage-policy set
// (the storage_policies_allowed M2M) with its default policy (storage_policy_id), so
// existing single-policy groups keep working after multi-policy support was added.
// Idempotent: it only adds the default when it is not already part of the set.
func migrateGroupAvailablePolicies(l logging.Logger, client *ent.Client, ctx context.Context) error {
l.Info("Backfilling group available storage policies...")
groups, err := client.Group.Query().WithStoragePoliciesAllowed().All(ctx)
if err != nil {
return fmt.Errorf("failed to query groups for policy backfill: %w", err)
}
for _, g := range groups {
if g.StoragePolicyID == 0 {
continue
}
already := false
for _, p := range g.Edges.StoragePoliciesAllowed {
if p.ID == g.StoragePolicyID {
already = true
break
}
}
if already {
continue
}
if err := client.Group.UpdateOne(g).AddStoragePoliciesAllowedIDs(g.StoragePolicyID).Exec(ctx); err != nil {
return fmt.Errorf("failed to backfill available policy for group %d: %w", g.ID, err)
}
}
return nil
}
func migrateAdminGroup(l logging.Logger, client *ent.Client, ctx context.Context) error {
if _, err := client.Group.Query().Where(group.ID(1)).First(ctx); err == nil {
l.Info("Default admin group (ID=1) already exists, skip migrating.")

@ -28,8 +28,11 @@ type (
StoragePolicyClient interface {
TxOperator
// GetByGroup returns the storage policies of the group.
// GetByGroup returns the default storage policy of the group.
GetByGroup(ctx context.Context, group *ent.Group) (*ent.StoragePolicy, error)
// ListPoliciesByGroup returns the set of storage policies a member of the group
// is allowed to upload to (the group's storage_policies_allowed edge).
ListPoliciesByGroup(ctx context.Context, group *ent.Group) ([]*ent.StoragePolicy, error)
// GetPolicyByID returns the storage policy by id.
GetPolicyByID(ctx context.Context, id int) (*ent.StoragePolicy, error)
// UpdateAccessKey updates the access key of the storage policy. It also clear related cache in KV.
@ -154,6 +157,36 @@ func (c *storagePolicyClient) GetByGroup(ctx context.Context, group *ent.Group)
return res, nil
}
// ListPoliciesByGroup returns the set of storage policies the group is allowed to
// upload to. The result always includes the group's default policy (storage_policy_id)
// even if it is not explicitly present in the allowed set, so callers can treat the
// returned slice as the authoritative allowed set.
func (c *storagePolicyClient) ListPoliciesByGroup(ctx context.Context, group *ent.Group) ([]*ent.StoragePolicy, error) {
res, err := withStoragePolicyEagerLoading(ctx, c.client.Group.QueryStoragePoliciesAllowed(group)).WithNode().All(ctx)
if err != nil {
return nil, fmt.Errorf("list allowed storage policies: %w", err)
}
// Ensure the default policy is always part of the allowed set.
if group.StoragePolicyID != 0 {
found := false
for _, p := range res {
if p.ID == group.StoragePolicyID {
found = true
break
}
}
if !found {
def, err := c.GetPolicyByID(ctx, group.StoragePolicyID)
if err == nil && def != nil {
res = append(res, def)
}
}
}
return res, nil
}
// GetPolicyByID returns the storage policy by id.
func (c *storagePolicyClient) GetPolicyByID(ctx context.Context, id int) (*ent.StoragePolicy, error) {
val, skipCache := ctx.Value(SkipStoragePolicyCache{}).(bool)

@ -0,0 +1,159 @@
package inventory
import (
"context"
"testing"
"github.com/cloudreve/Cloudreve/v4/ent"
"github.com/cloudreve/Cloudreve/v4/ent/enttest"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
"github.com/cloudreve/Cloudreve/v4/pkg/boolset"
"github.com/cloudreve/Cloudreve/v4/pkg/logging"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// newTestClient opens an in-memory SQLite ent client with the schema created.
func newTestClient(t *testing.T) *ent.Client {
t.Helper()
client := enttest.Open(t, "sqlite3", "file:apptest?mode=memory&cache=shared&_fk=1")
t.Cleanup(func() { _ = client.Close() })
return client
}
func makePolicy(t *testing.T, client *ent.Client, name string) *ent.StoragePolicy {
t.Helper()
p, err := client.StoragePolicy.Create().SetName(name).SetType("local").Save(context.Background())
require.NoError(t, err)
return p
}
// TestMultiStoragePolicyPerGroup covers APP-100: a group may hold several allowed
// policies, ListPoliciesByGroup returns the full set (always including the default),
// and membership can be decided from it.
func TestMultiStoragePolicyPerGroup(t *testing.T) {
ctx := context.Background()
client := newTestClient(t)
gc := NewGroupClient(client, "sqlite", nil)
sc := NewStoragePolicyClient(client, nil)
p1 := makePolicy(t, client, "local-1")
p2 := makePolicy(t, client, "s3-2")
p3 := makePolicy(t, client, "unassigned-3")
// Create a group with default = p1 and allowed = {p1, p2}.
created, err := gc.Upsert(ctx, &ent.Group{
Name: "multi",
Permissions: &boolset.BooleanSet{},
Settings: &types.GroupSetting{},
Edges: ent.GroupEdges{
StoragePolicies: p1,
StoragePoliciesAllowed: []*ent.StoragePolicy{p1, p2},
},
})
require.NoError(t, err)
// The allowed set must be exactly {p1, p2}.
allowed, err := sc.ListPoliciesByGroup(ctx, created)
require.NoError(t, err)
ids := policyIDSet(allowed)
assert.ElementsMatch(t, []int{p1.ID, p2.ID}, keysOf(ids), "allowed set should be {p1,p2}")
// Membership: p1/p2 allowed, p3 rejected (the guard used at upload time).
assert.True(t, ids[p1.ID], "p1 should be allowed")
assert.True(t, ids[p2.ID], "p2 should be allowed")
assert.False(t, ids[p3.ID], "p3 must NOT be allowed (out-of-set upload rejected)")
// The default remains p1.
def, err := sc.GetByGroup(ctx, created)
require.NoError(t, err)
assert.Equal(t, p1.ID, def.ID)
}
// TestSinglePolicyGroupNoRegression covers the "no regression" criterion: a group
// created with only a default policy still resolves that policy as its allowed set.
func TestSinglePolicyGroupNoRegression(t *testing.T) {
ctx := context.Background()
client := newTestClient(t)
gc := NewGroupClient(client, "sqlite", nil)
sc := NewStoragePolicyClient(client, nil)
p1 := makePolicy(t, client, "only")
created, err := gc.Upsert(ctx, &ent.Group{
Name: "single",
Permissions: &boolset.BooleanSet{},
Settings: &types.GroupSetting{},
Edges: ent.GroupEdges{StoragePolicies: p1},
})
require.NoError(t, err)
allowed, err := sc.ListPoliciesByGroup(ctx, created)
require.NoError(t, err)
ids := policyIDSet(allowed)
// Even though no allowed edge was set, the default is guaranteed to be a member.
assert.ElementsMatch(t, []int{p1.ID}, keysOf(ids))
}
// TestBackfillGroupAvailablePolicies verifies the idempotent migration that seeds the
// allowed set from the legacy single storage_policy_id.
func TestBackfillGroupAvailablePolicies(t *testing.T) {
ctx := context.Background()
client := newTestClient(t)
sc := NewStoragePolicyClient(client, nil)
p1 := makePolicy(t, client, "legacy")
// Legacy group: only storage_policy_id set, no allowed edge.
legacy, err := client.Group.Create().
SetName("legacy").
SetPermissions(&boolset.BooleanSet{}).
SetSettings(&types.GroupSetting{}).
SetStoragePolicyID(p1.ID).
Save(ctx)
require.NoError(t, err)
// Before backfill: empty allowed set at the edge level.
pre, err := client.Group.QueryStoragePoliciesAllowed(legacy).All(ctx)
require.NoError(t, err)
assert.Len(t, pre, 0)
// Run the backfill twice to assert idempotency.
require.NoError(t, migrateGroupAvailablePolicies(logging.NewConsoleLogger(logging.LevelError), client, ctx))
require.NoError(t, migrateGroupAvailablePolicies(logging.NewConsoleLogger(logging.LevelError), client, ctx))
post, err := client.Group.QueryStoragePoliciesAllowed(legacy).All(ctx)
require.NoError(t, err)
assert.ElementsMatch(t, []int{p1.ID}, policyIDsOf(post), "backfill should seed the default into the allowed set exactly once")
// And ListPoliciesByGroup now reflects it.
reloaded, err := client.Group.Get(ctx, legacy.ID)
require.NoError(t, err)
allowed, err := sc.ListPoliciesByGroup(ctx, reloaded)
require.NoError(t, err)
assert.ElementsMatch(t, []int{p1.ID}, policyIDsOf(allowed))
}
func policyIDSet(ps []*ent.StoragePolicy) map[int]bool {
m := make(map[int]bool, len(ps))
for _, p := range ps {
m[p.ID] = true
}
return m
}
func keysOf(m map[int]bool) []int {
out := make([]int, 0, len(m))
for k := range m {
out = append(out, k)
}
return out
}
func policyIDsOf(ps []*ent.StoragePolicy) []int {
out := make([]int, 0, len(ps))
for _, p := range ps {
out = append(out, p.ID)
}
return out
}

@ -202,11 +202,20 @@ func (f *DBFS) List(ctx context.Context, path *fs.URI, opts ...fs.Option) (fs.Fi
}
var storagePolicy *ent.StoragePolicy
var availablePolicies []*ent.StoragePolicy
if parent != nil {
storagePolicy, err = f.getPreferredPolicy(ctx, parent)
if err != nil {
f.l.Warning("Failed to get preferred policy: %v", err)
}
if ownerGroup := parent.Owner().Edges.Group; ownerGroup != nil {
sc, _ := inventory.InheritTx(ctx, f.storagePolicyClient)
availablePolicies, err = sc.ListPoliciesByGroup(ctx, ownerGroup)
if err != nil {
f.l.Warning("Failed to get available storage policies: %v", err)
}
}
}
return parent, &fs.ListFileResult{
@ -219,9 +228,10 @@ func (f *DBFS) List(ctx context.Context, path *fs.URI, opts ...fs.Option) (fs.Fi
RecursionLimitReached: children.RecursionLimitReached,
MixedType: children.MixedType,
SingleFileView: children.SingleFileView,
Parent: parent,
StoragePolicy: storagePolicy,
View: view,
Parent: parent,
StoragePolicy: storagePolicy,
AvailableStoragePolicies: availablePolicies,
View: view,
}, nil
}
@ -681,6 +691,31 @@ func (f *DBFS) getPreferredPolicy(ctx context.Context, file *File) (*ent.Storage
return groupPolicy, nil
}
// getAllowedPolicy resolves a client-requested storage policy for the given file,
// validating that the policy id belongs to the owner group's allowed set. It returns
// CodePolicyNotAllowed when the policy is not part of that set — this is the mandatory
// membership check that guards per-upload policy selection.
func (f *DBFS) getAllowedPolicy(ctx context.Context, file *File, policyID int) (*ent.StoragePolicy, error) {
ownerGroup := file.Owner().Edges.Group
if ownerGroup == nil {
return nil, fmt.Errorf("owner group not loaded")
}
sc, _ := inventory.InheritTx(ctx, f.storagePolicyClient)
allowed, err := sc.ListPoliciesByGroup(ctx, ownerGroup)
if err != nil {
return nil, serializer.NewError(serializer.CodeDBError, "Failed to get available storage policies", err)
}
for _, p := range allowed {
if p.ID == policyID {
return p, nil
}
}
return nil, serializer.NewError(serializer.CodePolicyNotAllowed, "The selected storage policy is not available for your group", nil)
}
func (f *DBFS) getFileByPath(ctx context.Context, navigator Navigator, path *fs.URI) (*File, error) {
file, err := navigator.To(ctx, path)
if err != nil && errors.Is(err, ErrFsNotInitialized) {

@ -121,7 +121,14 @@ func (f *DBFS) PrepareUpload(ctx context.Context, req *fs.UploadRequest, opts ..
policy *ent.StoragePolicy
)
if req.ImportFrom == nil {
policy, err = f.getPreferredPolicy(ctx, ancestor)
if req.Props.PreferredStoragePolicy > 0 {
// The client picked a specific policy: honor it only if it belongs to the
// owner group's allowed set, otherwise reject (never fall back silently).
policy, err = f.getAllowedPolicy(ctx, ancestor, req.Props.PreferredStoragePolicy)
} else {
// No explicit choice: use the group's default policy.
policy, err = f.getPreferredPolicy(ctx, ancestor)
}
} else {
policy, err = f.storagePolicyClient.GetPolicyByID(ctx, req.Props.PreferredStoragePolicy)
}

@ -224,7 +224,10 @@ type (
MixedType bool
SingleFileView bool
StoragePolicy *ent.StoragePolicy
View *types.ExplorerView
// AvailableStoragePolicies is the set of storage policies the current user's
// group is allowed to upload to. StoragePolicy above is the default one.
AvailableStoragePolicies []*ent.StoragePolicy
View *types.ExplorerView
}
// NavigatorProps is the properties of current filesystem.

@ -256,7 +256,10 @@ type ListResponse struct {
MixedType bool `json:"mixed_type"`
SingleFileView bool `json:"single_file_view,omitempty"`
StoragePolicy *StoragePolicy `json:"storage_policy,omitempty"`
View *types.ExplorerView `json:"view,omitempty"`
// AvailableStoragePolicies is the set of storage policies the current user's group
// may upload to. The uploader offers these as choices; StoragePolicy is the default.
AvailableStoragePolicies []*StoragePolicy `json:"available_storage_policies,omitempty"`
View *types.ExplorerView `json:"view,omitempty"`
}
type FileResponse struct {
@ -394,7 +397,10 @@ func BuildListResponse(ctx context.Context, u *ent.User, parent fs.File, res *fs
MixedType: res.MixedType,
SingleFileView: res.SingleFileView,
StoragePolicy: BuildStoragePolicy(res.StoragePolicy, hasher),
View: res.View,
AvailableStoragePolicies: lo.Map(res.AvailableStoragePolicies, func(sp *ent.StoragePolicy, _ int) *StoragePolicy {
return BuildStoragePolicy(sp, hasher)
}),
View: res.View,
}
if !res.Parent.IsNil() {

Loading…
Cancel
Save