Add a rudimentary filter option when listing entities

This change adds the ability to filter the list of entities returned
by the API by entity owner, name or endpoint, depending on the entity
type.

Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
This commit is contained in:
Gabriel Adrian Samfira 2025-06-17 22:37:18 +00:00
parent 2fd0e720e6
commit 499fbde60c
32 changed files with 879 additions and 73 deletions

View file

@ -111,13 +111,19 @@ func (s *sqlDatabase) GetEnterpriseByID(ctx context.Context, enterpriseID string
return param, nil
}
func (s *sqlDatabase) ListEnterprises(_ context.Context) ([]params.Enterprise, error) {
func (s *sqlDatabase) ListEnterprises(_ context.Context, filter params.EnterpriseFilter) ([]params.Enterprise, error) {
var enterprises []Enterprise
q := s.conn.
Preload("Credentials").
Preload("Credentials.Endpoint").
Preload("Endpoint").
Find(&enterprises)
Preload("Endpoint")
if filter.Name != "" {
q = q.Where("name = ?", filter.Name)
}
if filter.Endpoint != "" {
q = q.Where("endpoint_name = ?", filter.Endpoint)
}
q = q.Find(&enterprises)
if q.Error != nil {
return []params.Enterprise{}, errors.Wrap(q.Error, "fetching enterprises")
}

View file

@ -54,8 +54,10 @@ type EnterpriseTestSuite struct {
adminUserID string
testCreds params.ForgeCredentials
ghesCreds params.ForgeCredentials
secondaryTestCreds params.ForgeCredentials
githubEndpoint params.ForgeEndpoint
ghesEndpoint params.ForgeEndpoint
}
func (s *EnterpriseTestSuite) equalInstancesByName(expected, actual []params.Instance) {
@ -90,7 +92,9 @@ func (s *EnterpriseTestSuite) SetupTest() {
s.Require().NotEmpty(s.adminUserID)
s.githubEndpoint = garmTesting.CreateDefaultGithubEndpoint(adminCtx, db, s.T())
s.ghesEndpoint = garmTesting.CreateGHESEndpoint(adminCtx, db, s.T())
s.testCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "new-creds", db, s.T(), s.githubEndpoint)
s.ghesCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "ghes-creds", db, s.T(), s.ghesEndpoint)
s.secondaryTestCreds = garmTesting.CreateTestGithubCredentials(adminCtx, "secondary-creds", db, s.T(), s.githubEndpoint)
// create some enterprise objects in the database, for testing purposes
@ -272,18 +276,68 @@ func (s *EnterpriseTestSuite) TestGetEnterpriseDBDecryptingErr() {
}
func (s *EnterpriseTestSuite) TestListEnterprises() {
enterprises, err := s.Store.ListEnterprises(s.adminCtx)
enterprises, err := s.Store.ListEnterprises(s.adminCtx, params.EnterpriseFilter{})
s.Require().Nil(err)
garmTesting.EqualDBEntityByName(s.T(), s.Fixtures.Enterprises, enterprises)
}
func (s *EnterpriseTestSuite) TestListEnterprisesWithFilter() {
enterprise, err := s.Store.CreateEnterprise(
s.adminCtx,
"test-enterprise",
s.ghesCreds,
"test-secret",
params.PoolBalancerTypeRoundRobin,
)
s.Require().NoError(err)
enterprise2, err := s.Store.CreateEnterprise(
s.adminCtx,
"test-enterprise",
s.testCreds,
"test-secret",
params.PoolBalancerTypeRoundRobin,
)
s.Require().NoError(err)
enterprise3, err := s.Store.CreateEnterprise(
s.adminCtx,
"test-enterprise2",
s.testCreds,
"test-secret",
params.PoolBalancerTypeRoundRobin,
)
s.Require().NoError(err)
enterprises, err := s.Store.ListEnterprises(s.adminCtx, params.EnterpriseFilter{
Name: "test-enterprise",
})
s.Require().Nil(err)
garmTesting.EqualDBEntityByName(s.T(), []params.Enterprise{enterprise, enterprise2}, enterprises)
enterprises, err = s.Store.ListEnterprises(s.adminCtx, params.EnterpriseFilter{
Name: "test-enterprise",
Endpoint: s.ghesEndpoint.Name,
})
s.Require().Nil(err)
garmTesting.EqualDBEntityByName(s.T(), []params.Enterprise{enterprise}, enterprises)
enterprises, err = s.Store.ListEnterprises(s.adminCtx, params.EnterpriseFilter{
Name: "test-enterprise2",
})
s.Require().Nil(err)
garmTesting.EqualDBEntityByName(s.T(), []params.Enterprise{enterprise3}, enterprises)
}
func (s *EnterpriseTestSuite) TestListEnterprisesDBFetchErr() {
s.Fixtures.SQLMock.
ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE `enterprises`.`deleted_at` IS NULL")).
WillReturnError(fmt.Errorf("fetching user from database mock error"))
_, err := s.StoreSQLMocked.ListEnterprises(s.adminCtx)
_, err := s.StoreSQLMocked.ListEnterprises(s.adminCtx, params.EnterpriseFilter{})
s.assertSQLMockExpectations()
s.Require().NotNil(err)

View file

@ -92,15 +92,23 @@ func (s *sqlDatabase) GetOrganization(ctx context.Context, name, endpointName st
return param, nil
}
func (s *sqlDatabase) ListOrganizations(_ context.Context) ([]params.Organization, error) {
func (s *sqlDatabase) ListOrganizations(_ context.Context, filter params.OrganizationFilter) ([]params.Organization, error) {
var orgs []Organization
q := s.conn.
Preload("Credentials").
Preload("GiteaCredentials").
Preload("Credentials.Endpoint").
Preload("GiteaCredentials.Endpoint").
Preload("Endpoint").
Find(&orgs)
Preload("Endpoint")
if filter.Name != "" {
q = q.Where("name = ?", filter.Name)
}
if filter.Endpoint != "" {
q = q.Where("endpoint_name = ?", filter.Endpoint)
}
q = q.Find(&orgs)
if q.Error != nil {
return []params.Organization{}, errors.Wrap(q.Error, "fetching org from database")
}

View file

@ -333,18 +333,74 @@ func (s *OrgTestSuite) TestGetOrganizationDBDecryptingErr() {
}
func (s *OrgTestSuite) TestListOrganizations() {
orgs, err := s.Store.ListOrganizations(s.adminCtx)
orgs, err := s.Store.ListOrganizations(s.adminCtx, params.OrganizationFilter{})
s.Require().Nil(err)
garmTesting.EqualDBEntityByName(s.T(), s.Fixtures.Orgs, orgs)
}
func (s *OrgTestSuite) TestListOrganizationsWithFilters() {
org, err := s.Store.CreateOrganization(
s.adminCtx,
"test-org",
s.testCreds,
"super secret",
params.PoolBalancerTypeRoundRobin,
)
s.Require().NoError(err)
org2, err := s.Store.CreateOrganization(
s.adminCtx,
"test-org",
s.testCredsGitea,
"super secret",
params.PoolBalancerTypeRoundRobin,
)
s.Require().NoError(err)
org3, err := s.Store.CreateOrganization(
s.adminCtx,
"test-org2",
s.testCreds,
"super secret",
params.PoolBalancerTypeRoundRobin,
)
s.Require().NoError(err)
orgs, err := s.Store.ListOrganizations(
s.adminCtx,
params.OrganizationFilter{
Name: "test-org",
})
s.Require().Nil(err)
garmTesting.EqualDBEntityByName(s.T(), []params.Organization{org, org2}, orgs)
orgs, err = s.Store.ListOrganizations(
s.adminCtx,
params.OrganizationFilter{
Name: "test-org",
Endpoint: s.giteaEndpoint.Name,
})
s.Require().Nil(err)
garmTesting.EqualDBEntityByName(s.T(), []params.Organization{org2}, orgs)
orgs, err = s.Store.ListOrganizations(
s.adminCtx,
params.OrganizationFilter{
Name: "test-org2",
})
s.Require().Nil(err)
garmTesting.EqualDBEntityByName(s.T(), []params.Organization{org3}, orgs)
}
func (s *OrgTestSuite) TestListOrganizationsDBFetchErr() {
s.Fixtures.SQLMock.
ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE `organizations`.`deleted_at` IS NULL")).
WillReturnError(fmt.Errorf("fetching user from database mock error"))
_, err := s.StoreSQLMocked.ListOrganizations(s.adminCtx)
_, err := s.StoreSQLMocked.ListOrganizations(s.adminCtx, params.OrganizationFilter{})
s.assertSQLMockExpectations()
s.Require().NotNil(err)

View file

@ -93,15 +93,24 @@ func (s *sqlDatabase) GetRepository(ctx context.Context, owner, name, endpointNa
return param, nil
}
func (s *sqlDatabase) ListRepositories(_ context.Context) ([]params.Repository, error) {
func (s *sqlDatabase) ListRepositories(_ context.Context, filter params.RepositoryFilter) ([]params.Repository, error) {
var repos []Repository
q := s.conn.
Preload("Credentials").
Preload("GiteaCredentials").
Preload("Credentials.Endpoint").
Preload("GiteaCredentials.Endpoint").
Preload("Endpoint").
Find(&repos)
Preload("Endpoint")
if filter.Owner != "" {
q = q.Where("owner = ?", filter.Owner)
}
if filter.Name != "" {
q = q.Where("name = ?", filter.Name)
}
if filter.Endpoint != "" {
q = q.Where("endpoint_name = ?", filter.Endpoint)
}
q = q.Find(&repos)
if q.Error != nil {
return []params.Repository{}, errors.Wrap(q.Error, "fetching user from database")
}

View file

@ -376,18 +376,99 @@ func (s *RepoTestSuite) TestGetRepositoryDBDecryptingErr() {
}
func (s *RepoTestSuite) TestListRepositories() {
repos, err := s.Store.ListRepositories(s.adminCtx)
repos, err := s.Store.ListRepositories(s.adminCtx, params.RepositoryFilter{})
s.Require().Nil(err)
s.equalReposByName(s.Fixtures.Repos, repos)
}
func (s *RepoTestSuite) TestListRepositoriesWithFilters() {
repo, err := s.Store.CreateRepository(
s.adminCtx,
"test-owner",
"test-repo",
s.testCreds,
"super secret",
params.PoolBalancerTypeRoundRobin,
)
s.Require().NoError(err)
repo2, err := s.Store.CreateRepository(
s.adminCtx,
"test-owner",
"test-repo",
s.testCredsGitea,
"super secret",
params.PoolBalancerTypeRoundRobin,
)
s.Require().NoError(err)
repo3, err := s.Store.CreateRepository(
s.adminCtx,
"test-owner",
"test-repo2",
s.testCreds,
"super secret",
params.PoolBalancerTypeRoundRobin,
)
s.Require().NoError(err)
repo4, err := s.Store.CreateRepository(
s.adminCtx,
"test-owner2",
"test-repo",
s.testCreds,
"super secret",
params.PoolBalancerTypeRoundRobin,
)
s.Require().NoError(err)
repos, err := s.Store.ListRepositories(
s.adminCtx,
params.RepositoryFilter{
Name: "test-repo",
})
s.Require().Nil(err)
s.equalReposByName([]params.Repository{repo, repo2, repo4}, repos)
repos, err = s.Store.ListRepositories(
s.adminCtx,
params.RepositoryFilter{
Name: "test-repo",
Owner: "test-owner",
})
s.Require().Nil(err)
s.equalReposByName([]params.Repository{repo, repo2}, repos)
repos, err = s.Store.ListRepositories(
s.adminCtx,
params.RepositoryFilter{
Name: "test-repo",
Owner: "test-owner",
Endpoint: s.giteaEndpoint.Name,
})
s.Require().Nil(err)
s.equalReposByName([]params.Repository{repo2}, repos)
repos, err = s.Store.ListRepositories(
s.adminCtx,
params.RepositoryFilter{
Name: "test-repo2",
})
s.Require().Nil(err)
s.equalReposByName([]params.Repository{repo3}, repos)
}
func (s *RepoTestSuite) TestListRepositoriesDBFetchErr() {
s.Fixtures.SQLMock.
ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE `repositories`.`deleted_at` IS NULL")).
WillReturnError(fmt.Errorf("fetching user from database mock error"))
_, err := s.StoreSQLMocked.ListRepositories(s.adminCtx)
_, err := s.StoreSQLMocked.ListRepositories(s.adminCtx, params.RepositoryFilter{})
s.Require().NotNil(err)
s.Require().Equal("fetching user from database: fetching user from database mock error", err.Error())
@ -401,7 +482,7 @@ func (s *RepoTestSuite) TestListRepositoriesDBDecryptingErr() {
ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE `repositories`.`deleted_at` IS NULL")).
WillReturnRows(sqlmock.NewRows([]string{"id", "webhook_secret"}).AddRow(s.Fixtures.Repos[0].ID, s.Fixtures.Repos[0].WebhookSecret))
_, err := s.StoreSQLMocked.ListRepositories(s.adminCtx)
_, err := s.StoreSQLMocked.ListRepositories(s.adminCtx, params.RepositoryFilter{})
s.Require().NotNil(err)
s.Require().Equal("fetching repositories: decrypting secret: invalid passphrase length (expected length 32 characters)", err.Error())