diff --git a/internal/iam/repository_scope.go b/internal/iam/repository_scope.go index e913beff8c..65d5e23efa 100644 --- a/internal/iam/repository_scope.go +++ b/internal/iam/repository_scope.go @@ -7,6 +7,7 @@ import ( "context" "crypto/rand" "database/sql" + stderr "errors" "fmt" "strings" "time" @@ -421,6 +422,14 @@ func (r *Repository) UpdateScope(ctx context.Context, scope *Scope, version uint } return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("for public id %s", scope.PublicId))) } + var childComponentErrs error + item := resource.(*Scope) + if err := setScopeStoragePolicyId(ctx, r.reader, item); err != nil && !errors.IsNotFoundError(err) { + childComponentErrs = stderr.Join(err) + } + if childComponentErrs != nil { + return nil, rowsUpdated, errors.Wrap(ctx, childComponentErrs, op, errors.WithMsg(fmt.Sprintf("failed to fetch one or more child components for %s", item.PublicId))) + } return resource.(*Scope), rowsUpdated, nil } @@ -439,6 +448,13 @@ func (r *Repository) LookupScope(ctx context.Context, withPublicId string, _ ... } return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for %s", withPublicId))) } + var childComponentErrs error + if err := setScopeStoragePolicyId(ctx, r.reader, &scope); err != nil && !errors.IsNotFoundError(err) { + childComponentErrs = stderr.Join(err) + } + if childComponentErrs != nil { + return nil, errors.Wrap(ctx, childComponentErrs, op, errors.WithMsg(fmt.Sprintf("failed to fetch one or more child components for %s", scope.PublicId))) + } return &scope, nil } @@ -492,7 +508,22 @@ func (r *Repository) listScopes(ctx context.Context, withParentIds []string, opt ) } dbOpts := []db.Option{db.WithLimit(limit), db.WithOrder("create_time desc, public_id desc")} - return r.queryScopes(ctx, whereClause, args, dbOpts...) + scopes, t, err := r.queryScopes(ctx, whereClause, args, dbOpts...) + if err != nil { + return scopes, t, err + } + + var childComponentErrs error + for _, scope := range scopes { + if err := setScopeStoragePolicyId(ctx, r.reader, scope); err != nil && !errors.IsNotFoundError(err) { + childComponentErrs = stderr.Join(err) + } + } + if childComponentErrs != nil { + return scopes, t, errors.Wrap(ctx, childComponentErrs, op, errors.WithMsg("failed to fetch one or more child components")) + } + + return scopes, t, nil } // listScopesRefresh lists scopes in the given scopes and supports the @@ -534,7 +565,22 @@ func (r *Repository) listScopesRefresh(ctx context.Context, updatedAfter time.Ti } dbOpts := []db.Option{db.WithLimit(limit), db.WithOrder("update_time desc, public_id desc")} - return r.queryScopes(ctx, whereClause, args, dbOpts...) + scopes, t, err := r.queryScopes(ctx, whereClause, args, dbOpts...) + if err != nil { + return scopes, t, err + } + + var childComponentErrs error + for _, scope := range scopes { + if err := setScopeStoragePolicyId(ctx, r.reader, scope); err != nil && !errors.IsNotFoundError(err) { + childComponentErrs = stderr.Join(err) + } + } + if childComponentErrs != nil { + return scopes, t, errors.Wrap(ctx, childComponentErrs, op, errors.WithMsg("failed to fetch one or more child components")) + } + + return scopes, t, nil } func (r *Repository) queryScopes(ctx context.Context, whereClause string, args []any, opt ...db.Option) ([]*Scope, time.Time, error) { @@ -584,6 +630,15 @@ func (r *Repository) ListScopesRecursively(ctx context.Context, rootScopeId stri if err != nil { return nil, errors.Wrap(ctx, err, op+":ListQuery") } + var childComponentErrs error + for _, scope := range scopes { + if err := setScopeStoragePolicyId(ctx, r.reader, scope); err != nil && !errors.IsNotFoundError(err) { + childComponentErrs = stderr.Join(err) + } + } + if childComponentErrs != nil { + return nil, errors.Wrap(ctx, childComponentErrs, op, errors.WithMsg("failed to fetch one or more child components")) + } return scopes, nil } diff --git a/internal/iam/repository_scope_storage_policy.go b/internal/iam/repository_scope_storage_policy.go new file mode 100644 index 0000000000..ab10fce554 --- /dev/null +++ b/internal/iam/repository_scope_storage_policy.go @@ -0,0 +1,28 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package iam + +import ( + "context" + "fmt" + + "github.com/hashicorp/boundary/internal/db" + "github.com/hashicorp/boundary/internal/errors" +) + +// setScopeStoragePolicyId fetches the storage policy associated with the given +// scope and sets its StoragePolicyId field if an association does exist. If no +// record is found, a RecordNotFound error is returned. +func setScopeStoragePolicyId(ctx context.Context, r db.Reader, scope *Scope) error { + const op = "iam.setScopeStoragePolicyId" + var policy *ScopePolicyStoragePolicy + if err := r.SearchWhere(ctx, &policy, "scope_id = ?", []any{scope.PublicId}); err != nil { + return errors.Wrap(ctx, err, op) + } + if policy.ScopePolicyStoragePolicy == nil { + return errors.New(ctx, errors.RecordNotFound, op, fmt.Sprintf("%s scope storage policy not found", scope.PublicId), errors.WithoutEvent()) + } + scope.StoragePolicyId = policy.GetStoragePolicyId() + return nil +} diff --git a/internal/iam/repository_scope_test.go b/internal/iam/repository_scope_test.go index 0054f1d6a5..e8c1ba5325 100644 --- a/internal/iam/repository_scope_test.go +++ b/internal/iam/repository_scope_test.go @@ -153,20 +153,21 @@ func Test_Repository_Scope_Update(t *testing.T) { require.NoError(err) assert.Empty(foundScope.GetDescription()) // should be "" after update in db assert.True(proto.Equal(foundScope, s)) + assert.Empty(s.StoragePolicyId) err = db.TestVerifyOplog(t, rw, s.PublicId, db.WithOperation(oplog.OpType_OP_TYPE_CREATE), db.WithCreateNotBefore(10*time.Second)) require.NoError(err) s.Name = "foo" + id s.Description = "desc-id" // not in the field mask paths - s, updatedRows, err := repo.UpdateScope(ctx, s, 1, []string{"Name"}) + s, updatedRows, err := repo.UpdateScope(ctx, s, s.Version, []string{"Name"}) require.NoError(err) assert.Equal(1, updatedRows) require.NotNil(s) assert.Equal("foo"+id, s.GetName()) + assert.Empty(s.StoragePolicyId) // TODO: This isn't empty because of ICU-490 -- when that is resolved, fix this // assert.Empty(s.GetDescription()) - foundScope, err = repo.LookupScope(ctx, s.PublicId) require.NoError(err) assert.Equal(foundScope.GetPublicId(), s.GetPublicId()) @@ -177,7 +178,7 @@ func Test_Repository_Scope_Update(t *testing.T) { s.Name = "test2" s.Description = "desc-id-2" - s, updatedRows, err = repo.UpdateScope(ctx, s, 2, []string{"Name", "Description"}) + s, updatedRows, err = repo.UpdateScope(ctx, s, s.Version, []string{"Name", "Description"}) require.NoError(err) assert.Equal(1, updatedRows) require.NotNil(s) @@ -222,6 +223,7 @@ func Test_Repository_Scope_Lookup(t *testing.T) { foundScope, err := repo.LookupScope(context.Background(), s.PublicId) require.NoError(err) assert.True(proto.Equal(foundScope, s)) + assert.Empty(s.StoragePolicyId) invalidId := testId(t) notFoundById, err := repo.LookupScope(context.Background(), invalidId) @@ -516,7 +518,10 @@ func Test_Repository_ListScopes(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert, require := assert.New(t), require.New(t) - db.TestDeleteWhere(t, conn, func() any { i := AllocScope(); ; return &i }(), "type = 'org'") + db.TestDeleteWhere(t, conn, func() any { + i := AllocScope() + return &i + }(), "type = 'org'") testOrgs := []*Scope{} for i := 0; i < tt.createCnt; i++ {