mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
add a UID to each correlation
This commit is contained in:
parent
1f2233ff37
commit
f865427587
@ -449,11 +449,11 @@ func (hs *HTTPServer) CreateCorrelation(c *models.ReqContext) response.Response
|
||||
if errors.Is(err, datasources.ErrDatasourceIsReadOnly) {
|
||||
return response.Error(http.StatusForbidden, "Data source is read only", nil)
|
||||
}
|
||||
if errors.Is(err, datasources.ErrCorrelationExists) {
|
||||
return response.Error(http.StatusConflict, fmt.Sprintf("Correlation to %s already exists", cmd.TargetUID), nil)
|
||||
if errors.Is(err, datasources.ErrCorrelationUidExists) {
|
||||
return response.Error(http.StatusConflict, fmt.Sprintf("Correlation with uid %s already exists", cmd.Uid), nil)
|
||||
}
|
||||
|
||||
return response.Error(http.StatusInternalServerError, "Failed to query datasources", err)
|
||||
return response.Error(http.StatusInternalServerError, "Failed to add correlation", err)
|
||||
}
|
||||
|
||||
// TODO: maybe this?
|
||||
|
@ -3,13 +3,14 @@ package datasources
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrDataSourceNotFound = errors.New("data source not found")
|
||||
ErrDataSourceNameExists = errors.New("data source with the same name already exists")
|
||||
ErrDataSourceUidExists = errors.New("data source with the same uid already exists")
|
||||
ErrDataSourceUpdatingOldVersion = errors.New("trying to update old version of datasource")
|
||||
ErrDataSourceAccessDenied = errors.New("data source access denied")
|
||||
ErrDataSourceFailedGenerateUniqueUid = errors.New("failed to generate unique datasource ID")
|
||||
ErrDataSourceIdentifierNotSet = errors.New("unique identifier and org id are needed to be able to get or delete a datasource")
|
||||
ErrDatasourceIsReadOnly = errors.New("data source is readonly, can only be updated from configuration")
|
||||
ErrCorrelationExists = errors.New("correlation to the same datasource already exists")
|
||||
ErrDataSourceNotFound = errors.New("data source not found")
|
||||
ErrDataSourceNameExists = errors.New("data source with the same name already exists")
|
||||
ErrDataSourceUidExists = errors.New("data source with the same uid already exists")
|
||||
ErrDataSourceUpdatingOldVersion = errors.New("trying to update old version of datasource")
|
||||
ErrDataSourceAccessDenied = errors.New("data source access denied")
|
||||
ErrDataSourceFailedGenerateUniqueUid = errors.New("failed to generate unique datasource ID")
|
||||
ErrDataSourceIdentifierNotSet = errors.New("unique identifier and org id are needed to be able to get or delete a datasource")
|
||||
ErrDatasourceIsReadOnly = errors.New("data source is readonly, can only be updated from configuration")
|
||||
ErrCorrelationUidExists = errors.New("correlation to the same uid already exists")
|
||||
ErrCorrelationFailedGenerateUniqueUid = errors.New("failed to generate unique correlation ID")
|
||||
)
|
||||
|
@ -31,6 +31,7 @@ const (
|
||||
type DsAccess string
|
||||
|
||||
type Correlation struct {
|
||||
Uid string `json:"uid"`
|
||||
Target string `json:"target"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
@ -40,6 +41,7 @@ type Correlation struct {
|
||||
// in the data_source table. It's not part of the Correlation model but needed when editing correlations to handle
|
||||
// concurrent edits.
|
||||
type CorrelationDTO struct {
|
||||
Uid string `json:"uid"`
|
||||
Target string `json:"target"`
|
||||
Label string `json:"label,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
@ -175,6 +177,7 @@ type CreateCorrelationCommand struct {
|
||||
Description string `json:"description"`
|
||||
Version int `json:"version"`
|
||||
|
||||
Uid string `json:"-"`
|
||||
SourceUID string `json:"-"`
|
||||
OrgID int64 `json:"-"`
|
||||
Result CorrelationDTO `json:"-"`
|
||||
@ -183,7 +186,7 @@ type CreateCorrelationCommand struct {
|
||||
// UpdateCorrelationsCommand updates a correlation
|
||||
type UpdateCorrelationsCommand struct {
|
||||
SourceUID string
|
||||
TargetUID string
|
||||
Uid string
|
||||
Correlations []Correlation
|
||||
OrgId int64
|
||||
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/secrets/kvstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
@ -271,13 +272,33 @@ func (s *Service) CreateCorrelation(ctx context.Context, cmd *datasources.Create
|
||||
return err
|
||||
}
|
||||
|
||||
for _, correlation := range ds.Correlations {
|
||||
if correlation.Target == cmd.TargetUID {
|
||||
return datasources.ErrCorrelationExists
|
||||
if cmd.Uid != "" {
|
||||
for _, correlation := range ds.Correlations {
|
||||
if correlation.Target == cmd.Uid {
|
||||
return datasources.ErrCorrelationUidExists
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uidMap := make(map[string]bool)
|
||||
for _, correlation := range ds.Correlations {
|
||||
uidMap[correlation.Uid] = true
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
uid := util.GenerateShortUID()
|
||||
if exists := uidMap[uid]; !exists {
|
||||
cmd.Uid = uid
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.Uid == "" {
|
||||
return datasources.ErrCorrelationFailedGenerateUniqueUid
|
||||
}
|
||||
}
|
||||
|
||||
newCorrelation := datasources.Correlation{
|
||||
Uid: cmd.Uid,
|
||||
Target: cmd.TargetUID,
|
||||
Label: cmd.Label,
|
||||
Description: cmd.Description,
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/values"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
// ConfigVersion is used to figure out which API version a config uses.
|
||||
@ -42,7 +43,7 @@ type upsertDataSourceFromConfig struct {
|
||||
BasicAuthUser string
|
||||
WithCredentials bool
|
||||
IsDefault bool
|
||||
Correlations []datasources.Correlation
|
||||
Correlations []interface{}
|
||||
JSONData map[string]interface{}
|
||||
SecureJSONData map[string]string
|
||||
Editable bool
|
||||
@ -123,18 +124,6 @@ func (cfg *configsV1) mapToDatasourceFromConfig(apiVersion int64) *configs {
|
||||
}
|
||||
|
||||
for _, ds := range cfg.Datasources {
|
||||
correlations := make([]datasources.Correlation, 0)
|
||||
for _, v := range ds.Correlations.Value() {
|
||||
field, ok := v.(map[string]interface{})
|
||||
|
||||
if ok {
|
||||
correlations = append(correlations, datasources.Correlation{
|
||||
Target: field["targetUid"].(string),
|
||||
Description: field["description"].(string),
|
||||
Label: field["label"].(string),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
r.Datasources = append(r.Datasources, &upsertDataSourceFromConfig{
|
||||
OrgID: ds.OrgID.Value(),
|
||||
@ -148,7 +137,7 @@ func (cfg *configsV1) mapToDatasourceFromConfig(apiVersion int64) *configs {
|
||||
BasicAuthUser: ds.BasicAuthUser.Value(),
|
||||
WithCredentials: ds.WithCredentials.Value(),
|
||||
IsDefault: ds.IsDefault.Value(),
|
||||
Correlations: correlations,
|
||||
Correlations: ds.Correlations.Value(),
|
||||
JSONData: ds.JSONData.Value(),
|
||||
SecureJSONData: ds.SecureJSONData.Value(),
|
||||
Editable: ds.Editable.Value(),
|
||||
@ -177,18 +166,6 @@ func (cfg *configsV0) mapToDatasourceFromConfig(apiVersion int64) *configs {
|
||||
}
|
||||
|
||||
for _, ds := range cfg.Datasources {
|
||||
correlations := make([]datasources.Correlation, 0)
|
||||
for _, v := range ds.Correlations {
|
||||
field, ok := v.(map[string]interface{})
|
||||
|
||||
if ok {
|
||||
correlations = append(correlations, datasources.Correlation{
|
||||
Target: field["targetUid"].(string),
|
||||
Description: field["description"].(string),
|
||||
Label: field["label"].(string),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
r.Datasources = append(r.Datasources, &upsertDataSourceFromConfig{
|
||||
OrgID: ds.OrgID,
|
||||
@ -202,7 +179,7 @@ func (cfg *configsV0) mapToDatasourceFromConfig(apiVersion int64) *configs {
|
||||
BasicAuthUser: ds.BasicAuthUser,
|
||||
WithCredentials: ds.WithCredentials,
|
||||
IsDefault: ds.IsDefault,
|
||||
Correlations: correlations,
|
||||
Correlations: ds.Correlations,
|
||||
JSONData: ds.JSONData,
|
||||
SecureJSONData: ds.SecureJSONData,
|
||||
Editable: ds.Editable,
|
||||
@ -240,7 +217,7 @@ func createInsertCommand(ds *upsertDataSourceFromConfig) *datasources.AddDataSou
|
||||
BasicAuthUser: ds.BasicAuthUser,
|
||||
WithCredentials: ds.WithCredentials,
|
||||
IsDefault: ds.IsDefault,
|
||||
Correlations: ds.Correlations,
|
||||
Correlations: makeCorrelations(ds.Correlations),
|
||||
JsonData: jsonData,
|
||||
SecureJsonData: ds.SecureJSONData,
|
||||
ReadOnly: !ds.Editable,
|
||||
@ -253,6 +230,35 @@ func createInsertCommand(ds *upsertDataSourceFromConfig) *datasources.AddDataSou
|
||||
return cmd
|
||||
}
|
||||
|
||||
func makeCorrelations(correlations []interface{}) []datasources.Correlation {
|
||||
ret := make([]datasources.Correlation, 0)
|
||||
uidMap := make(map[string]bool)
|
||||
|
||||
for _, v := range correlations {
|
||||
if field, ok := v.(map[string]interface{}); ok {
|
||||
|
||||
uid := ""
|
||||
for i := 0; i < 10; i++ {
|
||||
newUid := util.GenerateShortUID()
|
||||
if exists := uidMap[newUid]; !exists {
|
||||
uid = newUid
|
||||
uidMap[uid] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ret = append(ret, datasources.Correlation{
|
||||
Uid: uid,
|
||||
Target: field["targetUid"].(string),
|
||||
Description: field["description"].(string),
|
||||
Label: field["label"].(string),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func safeUIDFromName(name string) string {
|
||||
h := sha256.New()
|
||||
_, _ = h.Write([]byte(name))
|
||||
@ -282,7 +288,7 @@ func createUpdateCommand(ds *upsertDataSourceFromConfig, id int64) *datasources.
|
||||
BasicAuthUser: ds.BasicAuthUser,
|
||||
WithCredentials: ds.WithCredentials,
|
||||
IsDefault: ds.IsDefault,
|
||||
Correlations: ds.Correlations,
|
||||
Correlations: makeCorrelations(ds.Correlations),
|
||||
JsonData: jsonData,
|
||||
SecureJsonData: ds.SecureJSONData,
|
||||
ReadOnly: !ds.Editable,
|
||||
|
@ -8454,6 +8454,33 @@
|
||||
}
|
||||
},
|
||||
"/v1/provisioning/folder/{FolderUID}/rule-groups/{Group}": {
|
||||
"get": {
|
||||
"tags": ["provisioning"],
|
||||
"summary": "Get a rule group.",
|
||||
"operationId": "RouteGetAlertRuleGroup",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "FolderUID",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "Group",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/AlertRuleGroup"
|
||||
},
|
||||
"404": {
|
||||
"description": " Not found."
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"consumes": ["application/json"],
|
||||
"tags": ["provisioning"],
|
||||
@ -8476,15 +8503,15 @@
|
||||
"name": "Body",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AlertRuleGroup"
|
||||
"$ref": "#/definitions/AlertRuleGroupMetadata"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "AlertRuleGroup",
|
||||
"description": "AlertRuleGroupMetadata",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AlertRuleGroup"
|
||||
"$ref": "#/definitions/AlertRuleGroupMetadata"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
@ -9497,7 +9524,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"AlertRuleGroup": {
|
||||
"AlertRuleGroupMetadata": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"interval": {
|
||||
@ -9924,6 +9951,9 @@
|
||||
"target": {
|
||||
"type": "string"
|
||||
},
|
||||
"uid": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
@ -10109,7 +10139,7 @@
|
||||
"$ref": "#/definitions/ScheduleDTO"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/State"
|
||||
},
|
||||
"templateVars": {
|
||||
"type": "object"
|
||||
@ -12461,6 +12491,9 @@
|
||||
"type": "string",
|
||||
"example": "now-1h"
|
||||
},
|
||||
"publicDashboardAccessToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"queries": {
|
||||
"description": "queries.refId – Specifies an identifier of the query. Is optional and default to “A”.\nqueries.datasourceId – Specifies the data source to be queried. Each query in the request must have an unique datasourceId.\nqueries.maxDataPoints - Species maximum amount of data points that dashboard panel can render. Is optional and default to 100.\nqueries.intervalMs - Specifies the time interval in milliseconds of time series. Is optional and defaults to 1000.",
|
||||
"type": "array",
|
||||
@ -15094,8 +15127,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"URL": {
|
||||
"description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.",
|
||||
"type": "object",
|
||||
"title": "URL is a custom URL type that allows validation at configuration load time.",
|
||||
"title": "A URL represents a parsed URL (technically, a URI reference).",
|
||||
"properties": {
|
||||
"ForceQuery": {
|
||||
"type": "boolean"
|
||||
@ -15872,7 +15906,6 @@
|
||||
}
|
||||
},
|
||||
"alertGroup": {
|
||||
"description": "AlertGroup alert group",
|
||||
"type": "object",
|
||||
"required": ["alerts", "labels", "receiver"],
|
||||
"properties": {
|
||||
@ -15892,7 +15925,6 @@
|
||||
}
|
||||
},
|
||||
"alertGroups": {
|
||||
"description": "AlertGroups alert groups",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/alertGroup"
|
||||
@ -15983,6 +16015,7 @@
|
||||
"$ref": "#/definitions/Duration"
|
||||
},
|
||||
"gettableAlert": {
|
||||
"description": "GettableAlert gettable alert",
|
||||
"type": "object",
|
||||
"required": ["labels", "annotations", "endsAt", "fingerprint", "receivers", "startsAt", "status", "updatedAt"],
|
||||
"properties": {
|
||||
@ -16035,7 +16068,6 @@
|
||||
}
|
||||
},
|
||||
"gettableSilence": {
|
||||
"description": "GettableSilence gettable silence",
|
||||
"type": "object",
|
||||
"required": ["comment", "createdBy", "endsAt", "matchers", "startsAt", "id", "status", "updatedAt"],
|
||||
"properties": {
|
||||
@ -16177,7 +16209,6 @@
|
||||
}
|
||||
},
|
||||
"postableSilence": {
|
||||
"description": "PostableSilence postable silence",
|
||||
"type": "object",
|
||||
"required": ["comment", "createdBy", "endsAt", "matchers", "startsAt"],
|
||||
"properties": {
|
||||
@ -16209,6 +16240,7 @@
|
||||
}
|
||||
},
|
||||
"receiver": {
|
||||
"description": "Receiver receiver",
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
|
@ -9054,6 +9054,9 @@
|
||||
"target": {
|
||||
"type": "string"
|
||||
},
|
||||
"uid": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
@ -9239,7 +9242,7 @@
|
||||
"$ref": "#/definitions/ScheduleDTO"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/State"
|
||||
},
|
||||
"templateVars": {
|
||||
"type": "object"
|
||||
@ -10791,6 +10794,9 @@
|
||||
"type": "string",
|
||||
"example": "now-1h"
|
||||
},
|
||||
"publicDashboardAccessToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"queries": {
|
||||
"description": "queries.refId – Specifies an identifier of the query. Is optional and default to “A”.\nqueries.datasourceId – Specifies the data source to be queried. Each query in the request must have an unique datasourceId.\nqueries.maxDataPoints - Species maximum amount of data points that dashboard panel can render. Is optional and default to 100.\nqueries.intervalMs - Specifies the time interval in milliseconds of time series. Is optional and defaults to 1000.",
|
||||
"type": "array",
|
||||
|
Loading…
Reference in New Issue
Block a user