grafana/pkg/services/correlations/models.go
Kristina 002f872ce1
Correlations: Allow correlations to target URLs (#92442)
* Pass one

* Fix linter and add new betterer problem (sorry)

* fix swagger

* Add type to tests and update single correlations sql

* Fix provisioning test and other function that needs a type

* Add errors around query/external typing and add tests

* increment number of correlations tested as we added one for testing v1 type placement

* try merging back the swagger that is in main

* try again?

* Style form a little

* Update public/app/features/logs/components/logParser.ts

Co-authored-by: Matias Chomicki <matyax@gmail.com>

* fix bad commit, simplify logic

* Demonstrating type difficulties

* Fix distributed union changes

* Additional type changes

* Update types in form

* Fix swagger

* Add comment around the assertion and explicit typing

---------

Co-authored-by: Matias Chomicki <matyax@gmail.com>
Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
2024-09-24 09:38:17 -05:00

293 lines
9.1 KiB
Go

package correlations
import (
"encoding/json"
"errors"
"fmt"
"github.com/grafana/grafana/pkg/services/quota"
)
var (
ErrCorrelationReadOnly = errors.New("correlation can only be edited via provisioning")
ErrSourceDataSourceDoesNotExists = errors.New("source data source does not exist")
ErrTargetDataSourceDoesNotExists = errors.New("target data source does not exist")
ErrCorrelationNotFound = errors.New("correlation not found")
ErrUpdateCorrelationEmptyParams = errors.New("not enough parameters to edit correlation")
ErrInvalidType = errors.New("invalid correlation type")
ErrInvalidTransformationType = errors.New("invalid transformation type")
ErrTransformationNotNested = errors.New("transformations must be nested under config")
ErrTransformationRegexReqExp = errors.New("regex transformations require expression")
ErrCorrelationsQuotaFailed = errors.New("error getting correlations quota")
ErrCorrelationsQuotaReached = errors.New("correlations quota reached")
ErrInvalidConfigType = errors.New("correlation contains non default value in config.type")
)
const (
QuotaTargetSrv quota.TargetSrv = "correlations"
QuotaTarget quota.Target = "correlations"
)
// the type of correlation, either query for containing query information, or external for containing an external URL
// +enum
type CorrelationType string
const (
query CorrelationType = "query"
external CorrelationType = "external"
)
type Transformation struct {
//Enum: regex,logfmt
Type string `json:"type"`
Expression string `json:"expression,omitempty"`
Field string `json:"field,omitempty"`
MapValue string `json:"mapValue,omitempty"`
}
func (t CorrelationType) Validate() error {
if t != query && t != external {
return fmt.Errorf("%s: \"%s\"", ErrInvalidType, t)
}
return nil
}
func (t Transformations) Validate() error {
for _, v := range t {
if v.Type != "regex" && v.Type != "logfmt" {
return fmt.Errorf("%s: \"%s\"", ErrInvalidTransformationType, t)
} else if v.Type == "regex" && len(v.Expression) == 0 {
return fmt.Errorf("%s: \"%s\"", ErrTransformationRegexReqExp, t)
}
}
return nil
}
type Transformations []Transformation
// swagger:model
type CorrelationConfig struct {
// Field used to attach the correlation link
// required:true
// example: message
Field string `json:"field" binding:"Required"`
// Target type
// This is deprecated: use the type property outside of config
// deprecated:true
Type CorrelationType `json:"type"`
// Target data query
// required:true
// example: {"prop1":"value1","prop2":"value"}
Target map[string]any `json:"target" binding:"Required"`
// Source data transformations
// required:false
// example: [{"type":"logfmt"}]
Transformations Transformations `json:"transformations,omitempty"`
}
func (c CorrelationConfig) MarshalJSON() ([]byte, error) {
target := c.Target
transformations := c.Transformations
if target == nil {
target = map[string]any{}
}
return json.Marshal(struct {
Field string `json:"field"`
Target map[string]any `json:"target"`
Transformations Transformations `json:"transformations,omitempty"`
}{
Field: c.Field,
Target: target,
Transformations: transformations,
})
}
// Correlation is the model for correlations definitions
// swagger:model
type Correlation struct {
// Unique identifier of the correlation
// example: 50xhMlg9k
UID string `json:"uid" xorm:"pk 'uid'"`
// UID of the data source the correlation originates from
// example: d0oxYRg4z
SourceUID string `json:"sourceUID" xorm:"pk 'source_uid'"`
// OrgID of the data source the correlation originates from
// Example: 1
OrgID int64 `json:"orgId" xorm:"pk 'org_id'"`
// UID of the data source the correlation points to
// example: PE1C5CBDA0504A6A3
TargetUID *string `json:"targetUID" xorm:"target_uid"`
// Label identifying the correlation
// example: My Label
Label string `json:"label" xorm:"label"`
// Description of the correlation
// example: Logs to Traces
Description string `json:"description" xorm:"description"`
// Correlation Configuration
Config CorrelationConfig `json:"config" xorm:"jsonb config"`
// Provisioned True if the correlation was created during provisioning
Provisioned bool `json:"provisioned"`
// The type of correlation. Currently, only valid value is "query"
Type CorrelationType `json:"type" binding:"Required"`
}
type GetCorrelationsResponseBody struct {
Correlations []Correlation `json:"correlations"`
TotalCount int64 `json:"totalCount"`
Page int64 `json:"page"`
Limit int64 `json:"limit"`
}
// CreateCorrelationResponse is the response struct for CreateCorrelationCommand
// swagger:model
type CreateCorrelationResponseBody struct {
Result Correlation `json:"result"`
// example: Correlation created
Message string `json:"message"`
}
// CreateCorrelationCommand is the command for creating a correlation
// swagger:model
type CreateCorrelationCommand struct {
// UID of the data source for which correlation is created.
SourceUID string `json:"-"`
OrgId int64 `json:"-"`
// Target data source UID to which the correlation is created. required if type = query
// example: PE1C5CBDA0504A6A3
TargetUID *string `json:"targetUID"`
// Optional label identifying the correlation
// example: My label
Label string `json:"label"`
// Optional description of the correlation
// example: Logs to Traces
Description string `json:"description"`
// Arbitrary configuration object handled in frontend
Config CorrelationConfig `json:"config" binding:"Required"`
// True if correlation was created with provisioning. This makes it read-only.
Provisioned bool `json:"provisioned"`
// correlation type
Type CorrelationType `json:"type" binding:"Required"`
}
func (c CreateCorrelationCommand) Validate() error {
if err := c.Type.Validate(); err != nil {
return err
}
if c.TargetUID == nil && c.Type == query {
return fmt.Errorf("correlations of type \"%s\" must have a targetUID", query)
}
if err := c.Config.Transformations.Validate(); err != nil {
return err
}
return nil
}
// swagger:model
type DeleteCorrelationResponseBody struct {
// example: Correlation deleted
Message string `json:"message"`
}
// DeleteCorrelationCommand is the command for deleting a correlation
type DeleteCorrelationCommand struct {
// UID of the correlation to be deleted.
UID string
SourceUID string
OrgId int64
}
// swagger:model
type UpdateCorrelationResponseBody struct {
Result Correlation `json:"result"`
// example: Correlation updated
Message string `json:"message"`
}
// swagger:model
type CorrelationConfigUpdateDTO struct {
// Field used to attach the correlation link
// example: message
Field *string `json:"field"`
// Target data query
// example: {"prop1":"value1","prop2":"value"}
Target *map[string]any `json:"target"`
// Source data transformations
// example: [{"type": "logfmt"},{"type":"regex","expression":"(Superman|Batman)", "variable":"name"}]
Transformations []Transformation `json:"transformations"`
}
// UpdateCorrelationCommand is the command for updating a correlation
// swagger:model
type UpdateCorrelationCommand struct {
// UID of the correlation to be updated.
UID string `json:"-"`
SourceUID string `json:"-"`
OrgId int64 `json:"-"`
// Optional label identifying the correlation
// example: My label
Label *string `json:"label"`
// Optional description of the correlation
// example: Logs to Traces
Description *string `json:"description"`
// Correlation Configuration
Config *CorrelationConfigUpdateDTO `json:"config"`
// correlation type
Type *CorrelationType `json:"type"`
}
func (c UpdateCorrelationCommand) Validate() error {
if c.Label == nil && c.Description == nil && c.Type == nil && (c.Config == nil || (c.Config.Field == nil && c.Config.Target == nil)) {
return ErrUpdateCorrelationEmptyParams
}
return nil
}
// GetCorrelationQuery is the query to retrieve a single correlation
type GetCorrelationQuery struct {
// UID of the correlation
UID string `json:"-"`
// UID of the source data source
SourceUID string `json:"-"`
OrgId int64 `json:"-"`
}
// GetCorrelationsBySourceUIDQuery is the query to retrieve all correlations originating by the given Data Source
type GetCorrelationsBySourceUIDQuery struct {
SourceUID string `json:"-"`
OrgId int64 `json:"-"`
}
// GetCorrelationsQuery is the query to retrieve all correlations
type GetCorrelationsQuery struct {
OrgId int64 `json:"-"`
// Limit the maximum number of correlations to return per page
// in:query
// required:false
// default:100
Limit int64 `json:"limit"`
// Page index for starting fetching correlations
// in:query
// required:false
// default:1
Page int64 `json:"page"`
// Source datasource UID filter to be applied to correlations
// in:query
// required:false
SourceUIDs []string `json:"sourceuid"`
}
type DeleteCorrelationsBySourceUIDCommand struct {
SourceUID string
OrgId int64
OnlyProvisioned bool
}
type DeleteCorrelationsByTargetUIDCommand struct {
TargetUID string
OrgId int64
}