From 3e933ebc2b0b6cad5df751048bb9ac1717da8a62 Mon Sep 17 00:00:00 2001 From: Rohil Surana Date: Fri, 17 Jul 2026 13:27:41 +0530 Subject: [PATCH] chore(resource): remove the unused resource-config cache (dead code) --- cmd/serve.go | 14 +- core/resource/mocks/config_repository.go | 95 ----------- core/resource/resource.go | 11 -- core/resource/service.go | 4 +- core/resource/service_test.go | 49 +++--- internal/store/blob/resources_repository.go | 180 -------------------- 6 files changed, 29 insertions(+), 324 deletions(-) delete mode 100644 core/resource/mocks/config_repository.go delete mode 100644 internal/store/blob/resources_repository.go diff --git a/cmd/serve.go b/cmd/serve.go index 5c3f77e20..ae2ac1304 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -112,7 +112,6 @@ import ( "github.com/pkg/profile" ) -var ruleCacheRefreshDelay = time.Minute * 2 var GetStripeClientFunc func(logger *slog.Logger, cfg *config.Frontier) *client.API func StartServer(logger *slog.Logger, cfg *config.Frontier) error { @@ -144,13 +143,9 @@ func StartServer(logger *slog.Logger, cfg *config.Frontier) error { if err != nil { return err } - resourceBlobRepository := blob.NewResourcesRepository(logger, resourceBlobFS) - if err := resourceBlobRepository.InitCache(ctx, ruleCacheRefreshDelay); err != nil { - return err - } defer func() { logger.Debug("cleaning up resource blob") - if err := resourceBlobRepository.Close(); err != nil { + if err := resourceBlobFS.Close(); err != nil { logger.Warn("resource blob cleanup failed", "err", err) } }() @@ -187,7 +182,7 @@ func StartServer(logger *slog.Logger, cfg *config.Frontier) error { return err } - deps, err := buildAPIDependencies(logger, cfg, dbClient, spiceDBClient, resourceBlobRepository, billingPlanRepository) + deps, err := buildAPIDependencies(logger, cfg, dbClient, spiceDBClient, resourceBlobFS, billingPlanRepository) if err != nil { return err } @@ -366,7 +361,7 @@ func buildAPIDependencies( cfg *config.Frontier, dbc *db.Client, sdb *spicedb.SpiceDB, - resourceBlobRepository *blob.ResourcesRepository, + resourceBlobBucket blob.Bucket, planBlobRepository *blob.PlanRepository, ) (api.Deps, error) { // Load additional traits from config file if specified @@ -541,7 +536,6 @@ func buildAPIDependencies( resourcePGRepository := postgres.NewResourceRepository(dbc) resourceService := resource.NewService( resourcePGRepository, - resourceBlobRepository, relationService, authnService, projectService, @@ -600,7 +594,7 @@ func buildAPIDependencies( usageService := usage.NewService(creditService) - resourceSchemaRepository := blob.NewSchemaConfigRepository(resourceBlobRepository.Bucket) + resourceSchemaRepository := blob.NewSchemaConfigRepository(resourceBlobBucket) bootstrapService := bootstrap.NewBootstrapService( logger, cfg.App.Admin, diff --git a/core/resource/mocks/config_repository.go b/core/resource/mocks/config_repository.go deleted file mode 100644 index d7c2162f8..000000000 --- a/core/resource/mocks/config_repository.go +++ /dev/null @@ -1,95 +0,0 @@ -// Code generated by mockery v2.53.5. DO NOT EDIT. - -package mocks - -import ( - context "context" - - resource "github.com/raystack/frontier/core/resource" - mock "github.com/stretchr/testify/mock" -) - -// ConfigRepository is an autogenerated mock type for the ConfigRepository type -type ConfigRepository struct { - mock.Mock -} - -type ConfigRepository_Expecter struct { - mock *mock.Mock -} - -func (_m *ConfigRepository) EXPECT() *ConfigRepository_Expecter { - return &ConfigRepository_Expecter{mock: &_m.Mock} -} - -// GetAll provides a mock function with given fields: ctx -func (_m *ConfigRepository) GetAll(ctx context.Context) ([]resource.YAML, error) { - ret := _m.Called(ctx) - - if len(ret) == 0 { - panic("no return value specified for GetAll") - } - - var r0 []resource.YAML - var r1 error - if rf, ok := ret.Get(0).(func(context.Context) ([]resource.YAML, error)); ok { - return rf(ctx) - } - if rf, ok := ret.Get(0).(func(context.Context) []resource.YAML); ok { - r0 = rf(ctx) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]resource.YAML) - } - } - - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// ConfigRepository_GetAll_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAll' -type ConfigRepository_GetAll_Call struct { - *mock.Call -} - -// GetAll is a helper method to define mock.On call -// - ctx context.Context -func (_e *ConfigRepository_Expecter) GetAll(ctx interface{}) *ConfigRepository_GetAll_Call { - return &ConfigRepository_GetAll_Call{Call: _e.mock.On("GetAll", ctx)} -} - -func (_c *ConfigRepository_GetAll_Call) Run(run func(ctx context.Context)) *ConfigRepository_GetAll_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) - }) - return _c -} - -func (_c *ConfigRepository_GetAll_Call) Return(_a0 []resource.YAML, _a1 error) *ConfigRepository_GetAll_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *ConfigRepository_GetAll_Call) RunAndReturn(run func(context.Context) ([]resource.YAML, error)) *ConfigRepository_GetAll_Call { - _c.Call.Return(run) - return _c -} - -// NewConfigRepository creates a new instance of ConfigRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewConfigRepository(t interface { - mock.TestingT - Cleanup(func()) -}) *ConfigRepository { - mock := &ConfigRepository{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/core/resource/resource.go b/core/resource/resource.go index ab074fd3d..344fa5057 100644 --- a/core/resource/resource.go +++ b/core/resource/resource.go @@ -19,10 +19,6 @@ type Repository interface { Delete(ctx context.Context, id string) error } -type ConfigRepository interface { - GetAll(ctx context.Context) ([]YAML, error) -} - type Resource struct { ID string `json:"id"` URN string `json:"urn"` @@ -42,13 +38,6 @@ func (res Resource) CreateURN(projectName string) string { return fmt.Sprintf("frn:%s:%s:%s", projectName, res.NamespaceID, res.Name) } -type YAML struct { - Name string `json:"name" yaml:"name"` - Backend string `json:"backend" yaml:"backend"` - ResourceType string `json:"resource_type" yaml:"resource_type"` - Actions map[string][]string `json:"actions" yaml:"actions"` -} - type Check struct { Object relation.Object Subject relation.Subject diff --git a/core/resource/service.go b/core/resource/service.go index 4671e2f55..e2c77d10d 100644 --- a/core/resource/service.go +++ b/core/resource/service.go @@ -45,7 +45,6 @@ type PATService interface { type Service struct { repository Repository - configRepository ConfigRepository relationService RelationService authnService AuthnService projectService ProjectService @@ -58,13 +57,12 @@ type AuditRecordRepository interface { Create(ctx context.Context, auditRecord auditmodels.AuditRecord) (auditmodels.AuditRecord, error) } -func NewService(repository Repository, configRepository ConfigRepository, +func NewService(repository Repository, relationService RelationService, authnService AuthnService, projectService ProjectService, orgService OrgService, patService PATService, auditRecordRepository AuditRecordRepository) *Service { return &Service{ repository: repository, - configRepository: configRepository, relationService: relationService, authnService: authnService, projectService: projectService, diff --git a/core/resource/service_test.go b/core/resource/service_test.go index bdad57694..cf1403fcc 100644 --- a/core/resource/service_test.go +++ b/core/resource/service_test.go @@ -20,23 +20,22 @@ import ( "github.com/stretchr/testify/mock" ) -func newTestService(t *testing.T) (*mocks.Repository, *mocks.ConfigRepository, *mocks.RelationService, *mocks.AuthnService, *mocks.ProjectService, *mocks.OrgService, *mocks.PATService, *mocks.AuditRecordRepository, *resource.Service) { +func newTestService(t *testing.T) (*mocks.Repository, *mocks.RelationService, *mocks.AuthnService, *mocks.ProjectService, *mocks.OrgService, *mocks.PATService, *mocks.AuditRecordRepository, *resource.Service) { t.Helper() repo := mocks.NewRepository(t) - configRepo := mocks.NewConfigRepository(t) relationSvc := mocks.NewRelationService(t) authnSvc := mocks.NewAuthnService(t) projectSvc := mocks.NewProjectService(t) orgSvc := mocks.NewOrgService(t) patSvc := mocks.NewPATService(t) auditRepo := mocks.NewAuditRecordRepository(t) - svc := resource.NewService(repo, configRepo, relationSvc, authnSvc, projectSvc, orgSvc, patSvc, auditRepo) - return repo, configRepo, relationSvc, authnSvc, projectSvc, orgSvc, patSvc, auditRepo, svc + svc := resource.NewService(repo, relationSvc, authnSvc, projectSvc, orgSvc, patSvc, auditRepo) + return repo, relationSvc, authnSvc, projectSvc, orgSvc, patSvc, auditRepo, svc } func TestCheckAuthz_NonPAT(t *testing.T) { ctx := context.Background() - _, _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) + _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) userID := uuid.New().String() orgID := uuid.New().String() @@ -62,7 +61,7 @@ func TestCheckAuthz_NonPAT(t *testing.T) { func TestCheckAuthz_PATScopeAllowed(t *testing.T) { ctx := context.Background() - _, _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) + _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) patID := uuid.New().String() userID := uuid.New().String() @@ -98,7 +97,7 @@ func TestCheckAuthz_PATScopeAllowed(t *testing.T) { func TestCheckAuthz_PATScopeDenied(t *testing.T) { ctx := context.Background() - _, _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) + _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) patID := uuid.New().String() userID := uuid.New().String() @@ -128,7 +127,7 @@ func TestCheckAuthz_PATScopeDenied(t *testing.T) { func TestCheckAuthz_PATScopeAllowed_UserDenied(t *testing.T) { ctx := context.Background() - _, _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) + _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) patID := uuid.New().String() userID := uuid.New().String() @@ -164,7 +163,7 @@ func TestCheckAuthz_PATScopeAllowed_UserDenied(t *testing.T) { func TestCheckAuthz_ExplicitPATSubject_ScopeAllowed(t *testing.T) { ctx := context.Background() - _, _, relationSvc, authnSvc, _, _, patSvc, _, svc := newTestService(t) + _, relationSvc, authnSvc, _, _, patSvc, _, svc := newTestService(t) patID := uuid.New().String() userID := uuid.New().String() @@ -207,7 +206,7 @@ func TestCheckAuthz_ExplicitPATSubject_ScopeAllowed(t *testing.T) { func TestCheckAuthz_ExplicitPATSubject_ScopeDenied(t *testing.T) { ctx := context.Background() - _, _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) + _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) patID := uuid.New().String() orgID := uuid.New().String() @@ -237,7 +236,7 @@ func TestCheckAuthz_ExplicitPATSubject_ScopeDenied(t *testing.T) { func TestBatchCheck_PATScopeAllowed(t *testing.T) { ctx := context.Background() - _, _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) + _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) patID := uuid.New().String() userID := uuid.New().String() @@ -282,7 +281,7 @@ func TestBatchCheck_PATScopeAllowed(t *testing.T) { func TestBatchCheck_PATScopeDenied(t *testing.T) { ctx := context.Background() - _, _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) + _, relationSvc, authnSvc, _, _, _, _, svc := newTestService(t) patID := uuid.New().String() userID := uuid.New().String() @@ -314,7 +313,7 @@ func TestBatchCheck_PATScopeDenied(t *testing.T) { func TestGet(t *testing.T) { t.Run("get by UUID calls GetByID", func(t *testing.T) { - repo, _, _, _, _, _, _, _, svc := newTestService(t) + repo, _, _, _, _, _, _, svc := newTestService(t) id := uuid.New().String() expected := resource.Resource{ID: id, Name: "test"} repo.EXPECT().GetByID(mock.Anything, id).Return(expected, nil) @@ -325,7 +324,7 @@ func TestGet(t *testing.T) { }) t.Run("get by URN calls GetByURN", func(t *testing.T) { - repo, _, _, _, _, _, _, _, svc := newTestService(t) + repo, _, _, _, _, _, _, svc := newTestService(t) urn := "frn:myproject:resource/item:myresource" expected := resource.Resource{URN: urn, Name: "myresource"} repo.EXPECT().GetByURN(mock.Anything, urn).Return(expected, nil) @@ -348,7 +347,7 @@ func TestCreate(t *testing.T) { } t.Run("creates resource with user principal", func(t *testing.T) { - repo, _, relationSvc, authnSvc, projectSvc, _, _, auditRepo, svc := newTestService(t) + repo, relationSvc, authnSvc, projectSvc, _, _, auditRepo, svc := newTestService(t) userID := uuid.New().String() authnSvc.EXPECT().GetPrincipal(mock.Anything, mock.Anything).Return(authenticate.Principal{ @@ -381,7 +380,7 @@ func TestCreate(t *testing.T) { }) t.Run("PAT principal resolves to user", func(t *testing.T) { - repo, _, relationSvc, authnSvc, projectSvc, _, patSvc, auditRepo, svc := newTestService(t) + repo, relationSvc, authnSvc, projectSvc, _, patSvc, auditRepo, svc := newTestService(t) patID := uuid.New().String() userID := uuid.New().String() @@ -420,7 +419,7 @@ func TestCreate(t *testing.T) { }) t.Run("explicit principal skips authn lookup", func(t *testing.T) { - repo, _, relationSvc, _, projectSvc, _, _, auditRepo, svc := newTestService(t) + repo, relationSvc, _, projectSvc, _, _, auditRepo, svc := newTestService(t) userID := uuid.New().String() projectSvc.EXPECT().Get(mock.Anything, testProject.ID).Return(testProject, nil) @@ -448,7 +447,7 @@ func TestCreate(t *testing.T) { func TestList(t *testing.T) { t.Run("delegates to repository", func(t *testing.T) { - repo, _, _, _, _, _, _, _, svc := newTestService(t) + repo, _, _, _, _, _, _, svc := newTestService(t) flt := resource.Filter{ProjectID: "proj-1"} expected := []resource.Resource{{ID: "r1"}, {ID: "r2"}} repo.EXPECT().List(mock.Anything, flt).Return(expected, nil) @@ -459,7 +458,7 @@ func TestList(t *testing.T) { }) t.Run("returns error from repository", func(t *testing.T) { - repo, _, _, _, _, _, _, _, svc := newTestService(t) + repo, _, _, _, _, _, _, svc := newTestService(t) repo.EXPECT().List(mock.Anything, mock.Anything).Return(nil, errors.New("db error")) _, err := svc.List(context.Background(), resource.Filter{}) @@ -469,7 +468,7 @@ func TestList(t *testing.T) { func TestUpdate(t *testing.T) { t.Run("delegates to repository", func(t *testing.T) { - repo, _, _, _, _, _, _, _, svc := newTestService(t) + repo, _, _, _, _, _, _, svc := newTestService(t) res := resource.Resource{ID: "r1", Title: "updated"} repo.EXPECT().Update(mock.Anything, res).Return(res, nil) @@ -481,7 +480,7 @@ func TestUpdate(t *testing.T) { func TestDelete(t *testing.T) { t.Run("deletes relations then resource", func(t *testing.T) { - repo, _, relationSvc, _, _, _, _, _, svc := newTestService(t) + repo, relationSvc, _, _, _, _, _, svc := newTestService(t) relationSvc.EXPECT().Delete(mock.Anything, relation.Relation{ Object: relation.Object{ID: "r1", Namespace: "resource/item"}, @@ -493,7 +492,7 @@ func TestDelete(t *testing.T) { }) t.Run("ignores relation not exist error", func(t *testing.T) { - repo, _, relationSvc, _, _, _, _, _, svc := newTestService(t) + repo, relationSvc, _, _, _, _, _, svc := newTestService(t) relationSvc.EXPECT().Delete(mock.Anything, mock.Anything).Return(relation.ErrNotExist) repo.EXPECT().Delete(mock.Anything, "r1").Return(nil) @@ -503,7 +502,7 @@ func TestDelete(t *testing.T) { }) t.Run("returns relation delete error", func(t *testing.T) { - _, _, relationSvc, _, _, _, _, _, svc := newTestService(t) + _, relationSvc, _, _, _, _, _, svc := newTestService(t) relationSvc.EXPECT().Delete(mock.Anything, mock.Anything).Return(errors.New("spicedb down")) @@ -514,7 +513,7 @@ func TestDelete(t *testing.T) { func TestAddProjectToResource(t *testing.T) { t.Run("creates project relation", func(t *testing.T) { - _, _, relationSvc, _, _, _, _, _, svc := newTestService(t) + _, relationSvc, _, _, _, _, _, svc := newTestService(t) relationSvc.EXPECT().Create(mock.Anything, relation.Relation{ Object: relation.Object{ID: "r1", Namespace: "resource/item"}, @@ -531,7 +530,7 @@ func TestAddProjectToResource(t *testing.T) { func TestAddResourceOwner(t *testing.T) { t.Run("creates owner relation with user principal", func(t *testing.T) { - _, _, relationSvc, _, _, _, _, _, svc := newTestService(t) + _, relationSvc, _, _, _, _, _, svc := newTestService(t) relationSvc.EXPECT().Create(mock.Anything, relation.Relation{ Object: relation.Object{ID: "r1", Namespace: "resource/item"}, diff --git a/internal/store/blob/resources_repository.go b/internal/store/blob/resources_repository.go deleted file mode 100644 index c8eb932fc..000000000 --- a/internal/store/blob/resources_repository.go +++ /dev/null @@ -1,180 +0,0 @@ -package blob - -import ( - "context" - "fmt" - "io" - "log/slog" - "strings" - "sync" - "time" - - "github.com/raystack/frontier/core/namespace" - "github.com/raystack/frontier/core/resource" - - "github.com/ghodss/yaml" - "github.com/pkg/errors" - "github.com/robfig/cron/v3" - - "gocloud.dev/blob" -) - -type ResourceBackends struct { - Backends []ResourceBackend `json:"backends" yaml:"backends"` -} - -type ResourceBackend struct { - Name string `json:"name" yaml:"name"` - ResourceTypes []ResourceType `json:"resource_types" yaml:"resource_types"` -} - -type ResourceType struct { - Name string `json:"name" yaml:"name"` - Actions map[string][]string `json:"actions" yaml:"actions"` -} - -type Resources struct { - Resources []Resource -} - -type Resource struct { - Name string - Actions map[string][]string -} - -// TODO(kushsharma): marked for deletion -type ResourcesRepository struct { - log *slog.Logger - mu *sync.Mutex - - cron *cron.Cron - Bucket Bucket - cached []resource.YAML -} - -func (repo *ResourcesRepository) GetAll(ctx context.Context) ([]resource.YAML, error) { - repo.mu.Lock() - currentCache := repo.cached - repo.mu.Unlock() - if repo.cron != nil { - // cache must have been refreshed automatically, just return - return currentCache, nil - } - - err := repo.refresh(ctx) - repo.mu.Lock() - currentCache = repo.cached - repo.mu.Unlock() - return currentCache, err -} - -func (repo *ResourcesRepository) GetRelationsForNamespace(ctx context.Context, namespaceID string) (map[string]bool, error) { - resources, err := repo.GetAll(ctx) - if err != nil { - return nil, err - } - - relationSet := map[string]bool{} - relationSet["organization"] = true - relationSet["project"] = true - relationSet["team"] = true - - for _, resource := range resources { - if resource.Name == namespaceID { - for _, action := range resource.Actions { - for _, relation := range action { - relationSet[fmt.Sprintf("%s_%s", namespaceID, relation)] = true - } - } - break - } - } - - if len(relationSet) == 0 { - return nil, fmt.Errorf("resource not found") - } - - return relationSet, err -} - -func (repo *ResourcesRepository) refresh(ctx context.Context) error { - var resources []resource.YAML - - // get all items - it := repo.Bucket.List(&blob.ListOptions{}) - for { - obj, err := it.Next(ctx) - if err != nil { - if err == io.EOF { - break - } - return err - } - - if obj.IsDir { - continue - } - if !(strings.HasSuffix(obj.Key, ".yaml") || strings.HasSuffix(obj.Key, ".yml")) { - continue - } - fileBytes, err := repo.Bucket.ReadAll(ctx, obj.Key) - if err != nil { - return errors.Wrap(err, "bucket.ReadAll: "+obj.Key) - } - - var resourceBackends ResourceBackends - if err := yaml.Unmarshal(fileBytes, &resourceBackends); err != nil { - return errors.Wrap(err, "yaml.Unmarshal: "+obj.Key) - } - if len(resourceBackends.Backends) == 0 { - continue - } - - for _, resourceBackend := range resourceBackends.Backends { - for _, resourceType := range resourceBackend.ResourceTypes { - resources = append(resources, resource.YAML{ - Name: namespace.CreateID(resourceBackend.Name, resourceType.Name), - Actions: resourceType.Actions, - Backend: resourceBackend.Name, - ResourceType: resourceType.Name, - }) - } - } - } - - repo.mu.Lock() - repo.cached = resources - repo.mu.Unlock() - repo.log.DebugContext(ctx, "resource config cache refreshed", "resource_config_count", len(resources)) - return nil -} - -func (repo *ResourcesRepository) InitCache(ctx context.Context, refreshDelay time.Duration) error { - repo.cron = cron.New(cron.WithChain( - cron.SkipIfStillRunning(cron.DefaultLogger), - )) - if _, err := repo.cron.AddFunc("@every "+refreshDelay.String(), func() { - if err := repo.refresh(ctx); err != nil { - repo.log.WarnContext(ctx, "failed to refresh resource config repository", "err", err) - } - }); err != nil { - return err - } - repo.cron.Start() - - // do it once right now - return repo.refresh(ctx) -} - -func (repo *ResourcesRepository) Close() error { - <-repo.cron.Stop().Done() - return repo.Bucket.Close() -} - -func NewResourcesRepository(logger *slog.Logger, b Bucket) *ResourcesRepository { - return &ResourcesRepository{ - log: logger, - Bucket: b, - mu: new(sync.Mutex), - } -}