diff --git a/apis/cmk/cmk-ui.yaml b/apis/cmk/cmk-ui.yaml index f12646a8..5058cfcd 100644 --- a/apis/cmk/cmk-ui.yaml +++ b/apis/cmk/cmk-ui.yaml @@ -698,6 +698,30 @@ paths: $ref: "#/components/responses/429" "500": $ref: "#/components/responses/500" + /systems/filterOptions: + get: + tags: + - Systems + summary: Get System Filter Values + description: Get system possible values for filters + operationId: getFilters + responses: + "200": + description: System retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/SystemFilters" + "400": + $ref: "#/components/responses/400" + "403": + $ref: "#/components/responses/403" + "404": + $ref: "#/components/responses/404" + "429": + $ref: "#/components/responses/429" + "500": + $ref: "#/components/responses/500" /systems/{systemID}: get: tags: @@ -1507,6 +1531,25 @@ components: enum: - RETRY - CANCEL + SystemFilters: + description: System Filters + type: object + properties: + region: + type: array + items: + type: string + description: List of existing system regions + type: + type: array + items: + type: string + description: List of existing system types + keyConfigurationName: + type: array + items: + type: string + description: List of existing system key configurations ClientCertificates: type: object properties: diff --git a/internal/api/cmkapi/cmkapi.go b/internal/api/cmkapi/cmkapi.go index 685e643f..438f06b8 100644 --- a/internal/api/cmkapi/cmkapi.go +++ b/internal/api/cmkapi/cmkapi.go @@ -710,6 +710,18 @@ type System struct { // SystemStatus The status of the System type SystemStatus string +// SystemFilters System Filters +type SystemFilters struct { + // KeyConfigurationName List of existing system key configurations + KeyConfigurationName *[]string `json:"keyConfigurationName,omitempty"` + + // Region List of existing system regions + Region *[]string `json:"region,omitempty"` + + // Type List of existing system types + Type *[]string `json:"type,omitempty"` +} + // ID The ID of System entity type ID = openapi_types.UUID @@ -1363,6 +1375,9 @@ type ServerInterface interface { // Retrieve all Systems // (GET /systems) GetAllSystems(w http.ResponseWriter, r *http.Request, params GetAllSystemsParams) + // Get System Filter Values + // (GET /systems/filterOptions) + GetFilters(w http.ResponseWriter, r *http.Request) // Retrieve a System // (GET /systems/{systemID}) GetSystemByID(w http.ResponseWriter, r *http.Request, systemID SystemIDPath) @@ -2221,6 +2236,20 @@ func (siw *ServerInterfaceWrapper) GetAllSystems(w http.ResponseWriter, r *http. handler.ServeHTTP(w, r) } +// GetFilters operation middleware +func (siw *ServerInterfaceWrapper) GetFilters(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetFilters(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // GetSystemByID operation middleware func (siw *ServerInterfaceWrapper) GetSystemByID(w http.ResponseWriter, r *http.Request) { @@ -2734,6 +2763,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H m.HandleFunc("GET "+options.BaseURL+"/keys/{keyID}/importParams", wrapper.GetKeyImportParams) m.HandleFunc("GET "+options.BaseURL+"/keys/{keyID}/versions", wrapper.GetKeyVersions) m.HandleFunc("GET "+options.BaseURL+"/systems", wrapper.GetAllSystems) + m.HandleFunc("GET "+options.BaseURL+"/systems/filterOptions", wrapper.GetFilters) m.HandleFunc("GET "+options.BaseURL+"/systems/{systemID}", wrapper.GetSystemByID) m.HandleFunc("DELETE "+options.BaseURL+"/systems/{systemID}/link", wrapper.UnlinkSystemAction) m.HandleFunc("PATCH "+options.BaseURL+"/systems/{systemID}/link", wrapper.LinkSystemAction) @@ -4343,6 +4373,66 @@ func (response GetAllSystems500JSONResponse) VisitGetAllSystemsResponse(w http.R return json.NewEncoder(w).Encode(response) } +type GetFiltersRequestObject struct { +} + +type GetFiltersResponseObject interface { + VisitGetFiltersResponse(w http.ResponseWriter) error +} + +type GetFilters200JSONResponse SystemFilters + +func (response GetFilters200JSONResponse) VisitGetFiltersResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetFilters400JSONResponse struct{ N400JSONResponse } + +func (response GetFilters400JSONResponse) VisitGetFiltersResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetFilters403JSONResponse struct{ N403JSONResponse } + +func (response GetFilters403JSONResponse) VisitGetFiltersResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(403) + + return json.NewEncoder(w).Encode(response) +} + +type GetFilters404JSONResponse struct{ N404JSONResponse } + +func (response GetFilters404JSONResponse) VisitGetFiltersResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type GetFilters429Response = N429Response + +func (response GetFilters429Response) VisitGetFiltersResponse(w http.ResponseWriter) error { + w.Header().Set("Retry-After", fmt.Sprint(response.Headers.RetryAfter)) + w.WriteHeader(429) + return nil +} + +type GetFilters500JSONResponse struct{ N500JSONResponse } + +func (response GetFilters500JSONResponse) VisitGetFiltersResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type GetSystemByIDRequestObject struct { SystemID SystemIDPath `json:"systemID"` } @@ -5369,6 +5459,9 @@ type StrictServerInterface interface { // Retrieve all Systems // (GET /systems) GetAllSystems(ctx context.Context, request GetAllSystemsRequestObject) (GetAllSystemsResponseObject, error) + // Get System Filter Values + // (GET /systems/filterOptions) + GetFilters(ctx context.Context, request GetFiltersRequestObject) (GetFiltersResponseObject, error) // Retrieve a System // (GET /systems/{systemID}) GetSystemByID(ctx context.Context, request GetSystemByIDRequestObject) (GetSystemByIDResponseObject, error) @@ -6190,6 +6283,30 @@ func (sh *strictHandler) GetAllSystems(w http.ResponseWriter, r *http.Request, p } } +// GetFilters operation middleware +func (sh *strictHandler) GetFilters(w http.ResponseWriter, r *http.Request) { + var request GetFiltersRequestObject + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetFilters(ctx, request.(GetFiltersRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetFilters") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetFiltersResponseObject); ok { + if err := validResponse.VisitGetFiltersResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // GetSystemByID operation middleware func (sh *strictHandler) GetSystemByID(w http.ResponseWriter, r *http.Request, systemID SystemIDPath) { var request GetSystemByIDRequestObject @@ -6736,97 +6853,99 @@ var swaggerSpec = []string{ "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", - "JHWTmC6pl+Th54f/HwAA//9cyaYuagsBAA==", + "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", + "DwAA//+JPX3HEw8BAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/authz/api_endpoint_mapping.go b/internal/authz/api_endpoint_mapping.go index 0a6ac505..ba7f972d 100644 --- a/internal/authz/api_endpoint_mapping.go +++ b/internal/authz/api_endpoint_mapping.go @@ -176,6 +176,12 @@ var allRestrictions = []Restricted{ APIResourceTypeName: APIResourceTypeSystem, APIAction: APIActionRead, }, + { + APIPath: "/systems/filterOptions", + APIMethod: APIMethodGet, + APIResourceTypeName: APIResourceTypeSystem, + APIAction: APIActionRead, + }, { APIPath: "/systems/{systemID}", APIMethod: APIMethodGet, diff --git a/internal/authz/repo/authz_repo.go b/internal/authz/repo/authz_repo.go index f6baf47a..f38c6750 100644 --- a/internal/authz/repo/authz_repo.go +++ b/internal/authz/repo/authz_repo.go @@ -10,9 +10,7 @@ import ( cmkcontext "github.com/openkcm/cmk/utils/context" ) -var ( - ErrUnauthorized = errors.New("action on resource unauthorized") -) +var ErrUnauthorized = errors.New("action on resource unauthorized") type AuthzRepo struct { repo repo.Repo @@ -21,7 +19,8 @@ type AuthzRepo struct { func NewAuthzRepo( repo repo.Repo, authzLoader *authz_loader.AuthzLoader[authz.RepoResourceTypeName, - authz.RepoAction]) *AuthzRepo { + authz.RepoAction], +) *AuthzRepo { return &AuthzRepo{ repo: repo, authzLoader: authzLoader, @@ -29,7 +28,8 @@ func NewAuthzRepo( } func (r *AuthzRepo) Create( - ctx context.Context, resource repo.Resource) error { + ctx context.Context, resource repo.Resource, +) error { err := r.checkResourceAuthZ(ctx, resource, authz.RepoActionCreate) if err != nil { return err @@ -136,8 +136,26 @@ func (r *AuthzRepo) Transaction(ctx context.Context, txFunc repo.TransactionFunc return r.repo.Transaction(ctx, txFunc) } +func (r *AuthzRepo) GetFilterOptions( + ctx context.Context, + resource repo.Resource, + columns []repo.Filter, + query repo.Query, +) error { + err := r.checkResourceAuthZ(ctx, resource, authz.RepoActionList) + if err != nil { + return err + } + err = r.checkQueryAuthZ(ctx, query, authz.RepoActionList) + if err != nil { + return err + } + return r.repo.GetFilterOptions(ctx, resource, columns, query) +} + func (r *AuthzRepo) checkResourceAuthZ( - ctx context.Context, resource repo.Resource, action authz.RepoAction) error { + ctx context.Context, resource repo.Resource, action authz.RepoAction, +) error { tenantID, err := cmkcontext.ExtractTenantID(ctx) if err != nil { return err @@ -159,7 +177,8 @@ func (r *AuthzRepo) checkResourceAuthZ( } func (r *AuthzRepo) checkQueryAuthZ( - ctx context.Context, query repo.Query, action authz.RepoAction) error { + ctx context.Context, query repo.Query, action authz.RepoAction, +) error { tenantID, err := cmkcontext.ExtractTenantID(ctx) if err != nil { return err diff --git a/internal/controllers/cmk/system_controller.go b/internal/controllers/cmk/system_controller.go index a375c0eb..23885d42 100644 --- a/internal/controllers/cmk/system_controller.go +++ b/internal/controllers/cmk/system_controller.go @@ -170,3 +170,15 @@ func (c *APIController) UnlinkSystemAction( return cmkapi.UnlinkSystemAction204Response(struct{}{}), nil } + +func (c *APIController) GetFilters( + ctx context.Context, + _ cmkapi.GetFiltersRequestObject, +) (cmkapi.GetFiltersResponseObject, error) { + filters, err := c.Manager.System.GetFilters(ctx) + if err != nil { + return nil, err + } + + return cmkapi.GetFilters200JSONResponse(filters), nil +} diff --git a/internal/controllers/cmk/system_controller_test.go b/internal/controllers/cmk/system_controller_test.go index 3c4b55a4..e6e1a47f 100644 --- a/internal/controllers/cmk/system_controller_test.go +++ b/internal/controllers/cmk/system_controller_test.go @@ -961,3 +961,44 @@ func TestUnlinkSystemAction(t *testing.T) { }) } } + +func TestGetFilters(t *testing.T) { + db, sv, tenant := startAPISystems(t, testutils.TestAPIServerConfig{}) + ctx := cmkcontext.CreateTenantContext(t.Context(), tenant) + r := sql.NewRepository(db) + + authClient := testutils.NewAuthClient(ctx, t, r, testutils.WithKeyAdminRole()) + + keyConfig1 := testutils.NewKeyConfig(func(k *model.KeyConfiguration) { + k.Name = "kcName1" + }, testutils.WithAuthClientDataKC(authClient)) + keyConfig2 := testutils.NewKeyConfig(func(k *model.KeyConfiguration) { + k.Name = "kcName2" + }, testutils.WithAuthClientDataKC(authClient)) + + system1 := testutils.NewSystem(func(s *model.System) { + s.Type = "type1" + s.Region = "region1" + s.KeyConfigurationID = &keyConfig1.ID + }) + + system2 := testutils.NewSystem(func(s *model.System) { + s.Type = "type2" + s.Region = "region2" + s.KeyConfigurationID = &keyConfig2.ID + }) + + testutils.CreateTestEntities(ctx, t, r, keyConfig1, keyConfig2, system1, system2) + + w := testutils.MakeHTTPRequest(t, sv, testutils.RequestOptions{ + Method: http.MethodGet, + Endpoint: "/systems/filterOptions", + Tenant: tenant, + AdditionalContext: authClient.GetClientMap(), + }) + + assert.Equal(t, http.StatusOK, w.Code) + + response := testutils.GetJSONBody[cmkapi.SystemFilters](t, w) + assert.NotNil(t, response) +} diff --git a/internal/manager/system.go b/internal/manager/system.go index 697082b3..6710db31 100644 --- a/internal/manager/system.go +++ b/internal/manager/system.go @@ -2,6 +2,7 @@ package manager import ( "context" + "fmt" "log/slog" "strings" @@ -43,6 +44,7 @@ type System interface { systemID uuid.UUID, action cmkapi.SystemRecoveryActionBodyAction, ) error + GetFilters(ctx context.Context) (cmkapi.SystemFilters, error) } type SystemManager struct { @@ -429,6 +431,63 @@ func (m *SystemManager) UnlinkSystemAction(ctx context.Context, systemID uuid.UU return nil } +func (m *SystemManager) GetFilters(ctx context.Context) (cmkapi.SystemFilters, error) { + var types []string + var regions []string + var keyConfigNames []string + + query := repo.NewQuery().Join(repo.LeftJoin, repo.JoinCondition{ + JoinTable: &model.KeyConfiguration{}, + JoinField: repo.IDField, + Table: &model.System{}, + Field: repo.KeyConfigIDField, + }) + filters := []repo.Filter{ + {Values: &types, Column: repo.TypeField}, + {Values: ®ions, Column: repo.RegionField}, + {Values: &keyConfigNames, Column: fmt.Sprintf("%s.%s", model.KeyConfiguration{}.TableName(), repo.NameField)}, + } + isGroupFiltered, err := m.user.NeedsGroupFiltering(ctx, authz.APIActionRead, authz.APIResourceTypeWorkFlow) + if err != nil { + return cmkapi.SystemFilters{}, err + } + + if isGroupFiltered { + // Only show systems linked to key configurations where the user has admin access + iamIdentifiers, err := cmkcontext.ExtractClientDataGroupsString(ctx) + if err != nil { + return cmkapi.SystemFilters{}, err + } + + // If IAM identifiers list is empty, user has no access to any key configurations + if len(iamIdentifiers) == 0 { + return cmkapi.SystemFilters{ + KeyConfigurationName: &keyConfigNames, + Region: ®ions, + Type: &types, + }, nil + } + + query = query.Join(repo.LeftJoin, repo.JoinCondition{ + Table: &model.KeyConfiguration{}, + Field: repo.AdminGroupIDField, + JoinField: repo.IDField, + JoinTable: &model.Group{}, + }) + + ck := repo.NewCompositeKey(). + Where(fmt.Sprintf(`"%s".%s`, model.Group{}.TableName(), repo.IAMIdField), iamIdentifiers) + query = query.Where(repo.NewCompositeKeyGroup(ck)) + } + err = m.repo.GetFilterOptions(ctx, model.System{}, filters, *query) + + return cmkapi.SystemFilters{ + KeyConfigurationName: &keyConfigNames, + Region: ®ions, + Type: &types, + }, err +} + func (m *SystemManager) UnmapSystemFromRegistry(ctx context.Context, system *model.System) error { tenant, err := cmkcontext.ExtractTenantID(ctx) if err != nil { diff --git a/internal/manager/system_test.go b/internal/manager/system_test.go index 0ca03a38..2ac572b2 100644 --- a/internal/manager/system_test.go +++ b/internal/manager/system_test.go @@ -131,16 +131,17 @@ func TestNewSystemManager(t *testing.T) { } func TestGetAllSystems(t *testing.T) { + groupID := uuid.NewString() + m, db, tenant := SetupSystemManager(t, nil) ctx := testutils.CreateCtxWithTenant(tenant) - ctx = testutils.InjectClientDataIntoContext(ctx, "test-user", []string{"test-group1"}) + ctx = testutils.InjectClientDataIntoContext(ctx, "test-user", []string{groupID}) r := sql.NewRepository(db) - testGroup := testutils.NewGroup( - func(g *model.Group) { - g.IAMIdentifier = "test-group1" - }, - ) + testGroup := testutils.NewGroup(func(g *model.Group) { + g.IAMIdentifier = groupID + }) + keyConfig := testutils.NewKeyConfig( func(k *model.KeyConfiguration) { k.AdminGroupID = testGroup.ID @@ -1508,3 +1509,59 @@ func TestRefreshSystems(t *testing.T) { }, ) } + +func TestGetFilters(t *testing.T) { + groupID := uuid.NewString() + m, db, tenant := SetupSystemManager(t, nil) + ctx := testutils.CreateCtxWithTenant(tenant) + ctx = testutils.InjectClientDataIntoContext(ctx, "test-user", []string{groupID}) + r := sql.NewRepository(db) + + testGroup := testutils.NewGroup(func(g *model.Group) { + g.IAMIdentifier = groupID + }) + + keyConfig1 := testutils.NewKeyConfig(func(k *model.KeyConfiguration) { + k.Name = "kcName1" + k.AdminGroup = *testGroup + }) + keyConfig2 := testutils.NewKeyConfig(func(k *model.KeyConfiguration) { + k.Name = "kcName2" + k.AdminGroup = *testGroup + }) + + keyConfig3 := testutils.NewKeyConfig(func(k *model.KeyConfiguration) { + k.Name = "kcName3" + }) + + system1 := testutils.NewSystem(func(s *model.System) { + s.Type = "type1" + s.Region = "region1" + s.KeyConfigurationID = &keyConfig1.ID + }) + + system2 := testutils.NewSystem(func(s *model.System) { + s.Type = "type2" + s.Region = "region2" + s.KeyConfigurationID = &keyConfig2.ID + }) + + testutils.CreateTestEntities(ctx, t, r, keyConfig1, keyConfig2, system1, system2, keyConfig3) + + t.Run("Should get filters for system for only user key configs", func(t *testing.T) { + filters, err := m.GetFilters(ctx) + assert.NoError(t, err) + + assert.Len(t, *filters.Type, 2) + assert.Contains(t, *filters.Type, system1.Type) + assert.Contains(t, *filters.Type, system2.Type) + + assert.Len(t, *filters.Region, 2) + assert.Contains(t, *filters.Region, system1.Region) + assert.Contains(t, *filters.Region, system2.Region) + + assert.Len(t, *filters.KeyConfigurationName, 2) + assert.Contains(t, *filters.KeyConfigurationName, keyConfig1.Name) + assert.Contains(t, *filters.KeyConfigurationName, keyConfig2.Name) + }) +} diff --git a/internal/manager/tenant_test.go b/internal/manager/tenant_test.go index 01b64cf7..d85485fd 100644 --- a/internal/manager/tenant_test.go +++ b/internal/manager/tenant_test.go @@ -392,6 +392,7 @@ func (s *mockSystemManager) UnlinkSystemAction(context.Context, uuid.UUID, strin func (s *mockSystemManager) GetAllSystems(context.Context, repo.QueryMapper) ([]*model.System, int, error) { panic("not implemented") } + func (s *mockSystemManager) GetSystemByID(context.Context, uuid.UUID) (*model.System, error) { panic("not implemented") } @@ -400,9 +401,11 @@ func (s *mockSystemManager) RefreshSystemsData(context.Context) bool { return tr func (s *mockSystemManager) LinkSystemAction(context.Context, uuid.UUID, cmkapi.SystemPatch) (*model.System, error) { panic("not implemented") } + func (s *mockSystemManager) GetRecoveryActions(context.Context, uuid.UUID) (cmkapi.SystemRecoveryAction, error) { panic("not implemented") } + func (s *mockSystemManager) SendRecoveryActions( context.Context, uuid.UUID, @@ -411,6 +414,10 @@ func (s *mockSystemManager) SendRecoveryActions( panic("not implemented") } +func (s *mockSystemManager) GetFilters(context.Context) (cmkapi.SystemFilters, error) { + panic("not implemented") +} + func disconnectAllExistingSystems(t *testing.T, ctx context.Context, r repo.Repo) { t.Helper() diff --git a/internal/repo/mock/repo.go b/internal/repo/mock/repo.go index 5ad962b0..bbdd3278 100644 --- a/internal/repo/mock/repo.go +++ b/internal/repo/mock/repo.go @@ -199,6 +199,15 @@ func (r *InMemoryRepository) OffboardTenant(_ context.Context, schemaName string return nil } +func (r *InMemoryRepository) GetFilterOptions( + ctx context.Context, + resource repo.Resource, + columns []repo.Filter, + query repo.Query, +) error { + return nil +} + func assignList(result any, list []repo.Resource) error { resultVal := reflect.ValueOf(result) if resultVal.Kind() != reflect.Ptr { diff --git a/internal/repo/query.go b/internal/repo/query.go index 3aae7150..67d838e5 100644 --- a/internal/repo/query.go +++ b/internal/repo/query.go @@ -306,6 +306,12 @@ type OrderField struct { Direction OrderDirection } +// Filter maps a struct field to a DB column for GetFilterOptions +type Filter struct { + Values *[]string + Column string +} + // NewQuery creates and returns a new empty query. func NewQuery() *Query { return &Query{ diff --git a/internal/repo/repository.go b/internal/repo/repository.go index 04b5abeb..22232e02 100644 --- a/internal/repo/repository.go +++ b/internal/repo/repository.go @@ -28,6 +28,7 @@ type Repo interface { Transaction(ctx context.Context, txFunc TransactionFunc) error Count(ctx context.Context, resource Resource, query Query) (int, error) OffboardTenant(ctx context.Context, tenantID string) error + GetFilterOptions(ctx context.Context, resource Resource, columns []Filter, query Query) error } // Resource defines the interface for Resource operations. diff --git a/internal/repo/sql/repo.go b/internal/repo/sql/repo.go index 2cfd785c..cb895b7e 100644 --- a/internal/repo/sql/repo.go +++ b/internal/repo/sql/repo.go @@ -32,6 +32,7 @@ const dbCtxKey ctxKey = "transactionRepo" var ( ErrUnsupportedOrderDirective = errors.New("unsupported order directive") + ErrFilterValuesEmpty = errors.New("filter must have a values field to populate") ) // ResourceRepository represents the repository for managing Resource data. @@ -358,6 +359,67 @@ func (r *ResourceRepository) Set(ctx context.Context, resource repo.Resource) er ) } +// GetFilterOptions populates the filters slice with the possible values for each column +func (r *ResourceRepository) GetFilterOptions( + ctx context.Context, + resource repo.Resource, + filters []repo.Filter, + query repo.Query, +) error { + if len(filters) == 0 { + return nil + } + + return r.WithTenant(ctx, resource, func(tx *multitenancy.DB) error { + parts := make([]any, 0, len(filters)) + placeholders := make([]string, 0, len(filters)) + columnMap := make(map[string]*[]string, len(filters)) + + db, err := applyQuery(tx.DB, resource, query) + if err != nil { + return err + } + + for i := range filters { + if filters[i].Values == nil { + return ErrFilterValuesEmpty + } + + col := filters[i].Column + columnMap[col] = filters[i].Values + + q := db.Session(&gorm.Session{}). + Table(resource.TableName()). + Select("? AS column_name, "+col+"::text AS value", col). + Where(col + " IS NOT NULL"). + Distinct() + + parts = append(parts, q) + placeholders = append(placeholders, "?") + } + + unionSQL := strings.Join(placeholders, " UNION ALL ") + + var results []struct { + ColumnName string `gorm:"column:column_name"` + Value string `gorm:"column:value"` + } + + tx = tx.Debug() + if err := tx.Raw(unionSQL, parts...).Scan(&results).Error; err != nil { + return errs.Wrap(repo.ErrGetResource, err) + } + + for _, row := range results { + if val, ok := columnMap[row.ColumnName]; ok { + *val = append(*val, row.Value) + } + } + + return nil + }) +} + // Transaction wraps a function inside a database transaction. // txFunc is a type TransactionFunc where we can define the transactional logic. // if txFunc return no error then transaction is committed,