Skip to content

Commit

Permalink
feat: MatchItem supports on all resource selector fields (#1296)
Browse files Browse the repository at this point in the history
* chore: add namespace to dummy configs

* feat: transform resource selector fields to PEG search

This is so that we get prefix/suffix support

* fix: remove allowed columns restriction and property lookup

* feat: support namespace lookup on search

* feat: remove column restriction

* chore: address review comments

* chore: property lookup should be explicit

field selector only selects fields

* chore: separate out tests for properties search
  • Loading branch information
adityathebe authored Jan 29, 2025
1 parent 85a43a6 commit 809777e
Show file tree
Hide file tree
Showing 10 changed files with 348 additions and 280 deletions.
2 changes: 1 addition & 1 deletion query/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func FindCheckIDs(ctx context.Context, limit int, resourceSelectors ...types.Res
}
}

return queryTableWithResourceSelectors(ctx, "checks", nil, limit, resourceSelectors...)
return queryTableWithResourceSelectors(ctx, "checks", limit, resourceSelectors...)
}

func GetChecksByIDs(ctx context.Context, ids []uuid.UUID) ([]models.Check, error) {
Expand Down
2 changes: 1 addition & 1 deletion query/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ func FindComponents(ctx context.Context, limit int, resourceSelectors ...types.R
}

func FindComponentIDs(ctx context.Context, limit int, resourceSelectors ...types.ResourceSelector) ([]uuid.UUID, error) {
return queryTableWithResourceSelectors(ctx, "components", models.AllowedColumnFieldsInComponents, limit, resourceSelectors...)
return queryTableWithResourceSelectors(ctx, "components", limit, resourceSelectors...)
}
2 changes: 1 addition & 1 deletion query/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ func FindConfigsByResourceSelector(ctx context.Context, limit int, resourceSelec
}

func FindConfigIDsByResourceSelector(ctx context.Context, limit int, resourceSelectors ...types.ResourceSelector) ([]uuid.UUID, error) {
return queryTableWithResourceSelectors(ctx, "config_items", models.AllowedColumnFieldsInConfigs, limit, resourceSelectors...)
return queryTableWithResourceSelectors(ctx, "config_items", limit, resourceSelectors...)
}

func FindConfigForComponent(ctx context.Context, componentID, configType string) ([]models.ConfigItem, error) {
Expand Down
74 changes: 52 additions & 22 deletions query/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,31 +68,52 @@ var CommonFields = map[string]func(ctx context.Context, tx *gorm.DB, val string)
}

type QueryModel struct {
Table string
Table string
Custom map[string]func(ctx context.Context, tx *gorm.DB, val string) (*gorm.DB, error)

// List of columns that are JSON type.
// These columns can be addressed using dot notation to access the JSON fields directly
// Example: tags.cluster or tags.namespace.
JSONColumns []string
DateFields []string
Columns []string

// List of columns that can be addressed on the search query.
// Any other fields will be treated as a property lookup.
Columns []string

// Alias maps fields from the search query to the table columns
Aliases map[string]string

// True when the table has a "tags" column
HasTags bool

// True when the table has a "labels" column
HasLabels bool

// True when the table has an "agent_id" column
HasAgents bool

// FieldMapper maps the value of these fields
FieldMapper map[string]func(ctx context.Context, id string) (any, error)
Custom map[string]func(ctx context.Context, tx *gorm.DB, val string) (*gorm.DB, error)
Aliases map[string]string
}

var ConfigQueryModel = QueryModel{
Table: "configs",
Columns: []string{
"name", "source", "type", "status", "health",
"name", "source", "type", "status", "agent_id", "health", "external_id", "config_class",
},
JSONColumns: []string{"labels", "tags", "config"},
JSONColumns: []string{"labels", "tags", "config", "properties"},
HasTags: true,
HasAgents: true,
HasLabels: true,
Aliases: map[string]string{
"created": "created_at",
"updated": "updated_at",
"deleted": "deleted_at",
"scraped": "last_scraped_time",
"agent": "agent_id",
"config_type": "type",
"namespace": "@namespace",
"namespace": "tags.namespace",
},

FieldMapper: map[string]func(ctx context.Context, id string) (any, error){
"agent_id": AgentMapper,
"created_at": DateMapper,
Expand Down Expand Up @@ -121,7 +142,7 @@ var ComponentQueryModel = QueryModel{
},
},
Columns: []string{
"name", "topology_id", "type", "status", "health",
"name", "namespace", "topology_id", "type", "status", "health",
},
JSONColumns: []string{"labels", "properties"},
Aliases: map[string]string{
Expand All @@ -131,9 +152,9 @@ var ComponentQueryModel = QueryModel{
"scraped": "last_scraped_time",
"agent": "agent_id",
"component_type": "type",
"namespace": "@namespace",
},

HasAgents: true,
HasLabels: true,
FieldMapper: map[string]func(ctx context.Context, id string) (any, error){
"agent_id": AgentMapper,
"created_at": DateMapper,
Expand All @@ -146,18 +167,19 @@ var ComponentQueryModel = QueryModel{
var CheckQueryModel = QueryModel{
Table: "checks",
Columns: []string{
"name", "canary_id", "type", "status",
"name", "namespace", "canary_id", "type", "status",
},
JSONColumns: []string{"labels"},
Aliases: map[string]string{
"created": "created_at",
"updated": "updated_at",
"deleted": "deleted_at",
"agent": "agent_id",
"health": "status",
"check_type": "type",
"namespace": "@namespace",
},

HasAgents: true,
HasLabels: true,
FieldMapper: map[string]func(ctx context.Context, id string) (any, error){
"agent_id": AgentMapper,
"created_at": DateMapper,
Expand All @@ -167,12 +189,13 @@ var CheckQueryModel = QueryModel{
}

var PlaybookQueryModel = QueryModel{
Table: models.Playbook{}.TableName(),
Table: models.Playbook{}.TableName(),
HasTags: true,
Columns: []string{"name", "namespace"},
Aliases: map[string]string{
"created": "created_at",
"updated": "updated_at",
"deleted": "deleted_at",
"namespace": "@namespace",
"created": "created_at",
"updated": "updated_at",
"deleted": "deleted_at",
},
FieldMapper: map[string]func(ctx context.Context, id string) (any, error){
"created_at": DateMapper,
Expand All @@ -198,7 +221,7 @@ func GetModelFromTable(table string) (QueryModel, error) {

// QueryModel.Apply will ignore these fields when converting to clauses
// as we modify the tx directly for them
var ignoreFieldsForClauses = []string{"sort", "offset", "limit", "labels", "config", "tags"}
var ignoreFieldsForClauses = []string{"sort", "offset", "limit", "labels", "config", "tags", "properties"}

func (qm QueryModel) Apply(ctx context.Context, q types.QueryField, tx *gorm.DB) (*gorm.DB, []clause.Expression, error) {
if tx == nil {
Expand Down Expand Up @@ -236,10 +259,17 @@ func (qm QueryModel) Apply(ctx context.Context, q types.QueryField, tx *gorm.DB)
}

for _, column := range qm.JSONColumns {
if strings.HasPrefix(originalField, column) {
// Keys in JSON fields are addressable as <column>.<key>
// exampel: labels.cluster or tags.namespace
if strings.HasPrefix(originalField, fmt.Sprintf("%s.", column)) {
tx = JSONPathMapper(ctx, tx, column, q.Op, strings.TrimPrefix(originalField, column+"."), val)
q.Field = column
}

if strings.HasPrefix(q.Field, fmt.Sprintf("%s.", column)) {
tx = JSONPathMapper(ctx, tx, column, q.Op, strings.TrimPrefix(q.Field, column+"."), val)
q.Field = column
}
}

if !slices.Contains(ignoreFieldsForClauses, q.Field) {
Expand Down
4 changes: 2 additions & 2 deletions query/playbooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import (
)

func FindPlaybookIDsByResourceSelector(ctx context.Context, limit int, resourceSelectors ...types.ResourceSelector) ([]uuid.UUID, error) {
return queryTableWithResourceSelectors(ctx, "playbooks", models.AllowedColumnFieldsInPlaybooks, limit, resourceSelectors...)
return queryTableWithResourceSelectors(ctx, "playbooks", limit, resourceSelectors...)
}

func FindPlaybooksByResourceSelector(ctx context.Context, limit int, resourceSelectors ...types.ResourceSelector) ([]models.Playbook, error) {
ids, err := queryTableWithResourceSelectors(ctx, "playbooks", models.AllowedColumnFieldsInPlaybooks, limit, resourceSelectors...)
ids, err := queryTableWithResourceSelectors(ctx, "playbooks", limit, resourceSelectors...)
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 809777e

Please sign in to comment.