Merge remote-tracking branch 'origin/main' into feat/round-tripper-extra-audience-option

This commit is contained in:
Roberto Jimenez Sanchez
2025-12-18 11:04:01 +01:00
87 changed files with 9302 additions and 741 deletions
@@ -1,6 +1,9 @@
package v0alpha1
TemplateKind: *"grafana" | "mimir"
TemplateGroupSpec: {
title: string
content: string
kind: TemplateKind
}
@@ -2,13 +2,24 @@
package v0alpha1
// +k8s:openapi-gen=true
type TemplateGroupTemplateKind string
const (
TemplateGroupTemplateKindGrafana TemplateGroupTemplateKind = "grafana"
TemplateGroupTemplateKindMimir TemplateGroupTemplateKind = "mimir"
)
// +k8s:openapi-gen=true
type TemplateGroupSpec struct {
Title string `json:"title"`
Content string `json:"content"`
Title string `json:"title"`
Content string `json:"content"`
Kind TemplateGroupTemplateKind `json:"kind"`
}
// NewTemplateGroupSpec creates a new TemplateGroupSpec object.
func NewTemplateGroupSpec() *TemplateGroupSpec {
return &TemplateGroupSpec{}
return &TemplateGroupSpec{
Kind: TemplateGroupTemplateKindGrafana,
}
}
@@ -26,7 +26,7 @@ var (
rawSchemaRoutingTreev0alpha1 = []byte(`{"Matcher":{"additionalProperties":false,"properties":{"label":{"type":"string"},"type":{"enum":["=","!=","=~","!~"],"type":"string"},"value":{"type":"string"}},"required":["type","label","value"],"type":"object"},"Route":{"additionalProperties":false,"properties":{"active_time_intervals":{"items":{"type":"string"},"type":"array"},"continue":{"type":"boolean"},"group_by":{"items":{"type":"string"},"type":"array"},"group_interval":{"type":"string"},"group_wait":{"type":"string"},"matchers":{"items":{"$ref":"#/components/schemas/Matcher"},"type":"array"},"mute_time_intervals":{"items":{"type":"string"},"type":"array"},"receiver":{"type":"string"},"repeat_interval":{"type":"string"},"routes":{"items":{"$ref":"#/components/schemas/Route"},"type":"array"}},"required":["continue"],"type":"object"},"RouteDefaults":{"additionalProperties":false,"properties":{"group_by":{"items":{"type":"string"},"type":"array"},"group_interval":{"type":"string"},"group_wait":{"type":"string"},"receiver":{"type":"string"},"repeat_interval":{"type":"string"}},"required":["receiver"],"type":"object"},"RoutingTree":{"properties":{"spec":{"$ref":"#/components/schemas/spec"}},"required":["spec"]},"spec":{"additionalProperties":false,"properties":{"defaults":{"$ref":"#/components/schemas/RouteDefaults"},"routes":{"items":{"$ref":"#/components/schemas/Route"},"type":"array"}},"required":["defaults","routes"],"type":"object"}}`)
versionSchemaRoutingTreev0alpha1 app.VersionSchema
_ = json.Unmarshal(rawSchemaRoutingTreev0alpha1, &versionSchemaRoutingTreev0alpha1)
rawSchemaTemplateGroupv0alpha1 = []byte(`{"TemplateGroup":{"properties":{"spec":{"$ref":"#/components/schemas/spec"}},"required":["spec"]},"spec":{"additionalProperties":false,"properties":{"content":{"type":"string"},"title":{"type":"string"}},"required":["title","content"],"type":"object"}}`)
rawSchemaTemplateGroupv0alpha1 = []byte(`{"TemplateGroup":{"properties":{"spec":{"$ref":"#/components/schemas/spec"}},"required":["spec"]},"TemplateKind":{"enum":["grafana","mimir"],"type":"string"},"spec":{"additionalProperties":false,"properties":{"content":{"type":"string"},"kind":{"$ref":"#/components/schemas/TemplateKind","default":"grafana"},"title":{"type":"string"}},"required":["title","content","kind"],"type":"object"}}`)
versionSchemaTemplateGroupv0alpha1 app.VersionSchema
_ = json.Unmarshal(rawSchemaTemplateGroupv0alpha1, &versionSchemaTemplateGroupv0alpha1)
rawSchemaTimeIntervalv0alpha1 = []byte(`{"Interval":{"additionalProperties":false,"properties":{"days_of_month":{"items":{"type":"string"},"type":"array"},"location":{"type":"string"},"months":{"items":{"type":"string"},"type":"array"},"times":{"items":{"$ref":"#/components/schemas/TimeRange"},"type":"array"},"weekdays":{"items":{"type":"string"},"type":"array"},"years":{"items":{"type":"string"},"type":"array"}},"type":"object"},"TimeInterval":{"properties":{"spec":{"$ref":"#/components/schemas/spec"}},"required":["spec"]},"TimeRange":{"additionalProperties":false,"properties":{"end_time":{"type":"string"},"start_time":{"type":"string"}},"required":["start_time","end_time"],"type":"object"},"spec":{"additionalProperties":false,"properties":{"name":{"type":"string"},"time_intervals":{"items":{"$ref":"#/components/schemas/Interval"},"type":"array"}},"required":["name","time_intervals"],"type":"object"}}`)
@@ -0,0 +1,156 @@
{
"apiVersion": "dashboard.grafana.app/v1beta1",
"kind": "Dashboard",
"metadata": {
"name": "ad5vfcn",
"namespace": "default",
"uid": "dlMZZl6GndU8gJLUQSmgZxXBPCNXyXhNBeQJhHXl0r4X",
"resourceVersion": "2",
"generation": 2,
"creationTimestamp": "2025-11-28T10:14:21Z",
"labels": {
"grafana.app/deprecatedInternalID": "288"
},
"annotations": {
"grafana.app/createdBy": "user:eex2ofwuj0agwd",
"grafana.app/updatedBy": "user:eex2ofwuj0agwd",
"grafana.app/updatedTimestamp": "2025-11-28T10:15:06Z"
}
},
"spec": {
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 288,
"links": [],
"panels": [
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.4.0-pre",
"targets": [
{
"datasource": {},
"queryType": "randomWalk"
},
{
"datasource": {},
"queryType": "randomWalk"
}
],
"title": "New panel",
"type": "timeseries"
}
],
"preload": false,
"schemaVersion": 42,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "Panel ds inheritance ",
"uid": "ad5vfcn",
"version": 2
},
"status": {}
}
@@ -0,0 +1,864 @@
{
"kind": "DashboardWithAccessInfo",
"apiVersion": "dashboard.grafana.app/v2beta1",
"metadata": {
"name": "value-mapping-test",
"namespace": "default",
"uid": "value-mapping-test",
"resourceVersion": "1765384157199094",
"generation": 2,
"creationTimestamp": "2025-11-19T20:09:28Z",
"labels": {
"grafana.app/deprecatedInternalID": "646372978987008"
}
},
"spec": {
"annotations": [
{
"kind": "AnnotationQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "grafana",
"version": "v0",
"datasource": {
"name": "-- Grafana --"
},
"spec": {}
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"builtIn": true,
"legacyOptions": {
"type": "dashboard"
}
}
}
],
"cursorSync": "Off",
"description": "Test dashboard for all value mapping types and override matcher types",
"editable": true,
"elements": {
"panel-1": {
"kind": "Panel",
"spec": {
"id": 1,
"title": "ValueMap Example",
"description": "Panel with ValueMap mapping type - maps specific text values to colors and display text",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "prometheus",
"version": "v0",
"datasource": {
"name": "prometheus-uid"
},
"spec": {
"expr": "up"
}
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "VizConfig",
"group": "stat",
"version": "",
"spec": {
"options": {},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "value",
"options": {
"critical": {
"text": "Critical!",
"color": "red",
"index": 0
},
"ok": {
"text": "OK",
"color": "green",
"index": 2
},
"warning": {
"text": "Warning",
"color": "orange",
"index": 1
}
}
}
]
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "status"
},
"properties": [
{
"id": "custom.width",
"value": 100
},
{
"id": "custom.align",
"value": "center"
}
]
}
]
}
}
}
}
},
"panel-2": {
"kind": "Panel",
"spec": {
"id": 2,
"title": "RangeMap Example",
"description": "Panel with RangeMap mapping type - maps numerical ranges to colors and display text",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "prometheus",
"version": "v0",
"datasource": {
"name": "prometheus-uid"
},
"spec": {
"expr": "cpu_usage_percent"
}
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "VizConfig",
"group": "gauge",
"version": "",
"spec": {
"options": {},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "range",
"options": {
"from": 0,
"to": 50,
"result": {
"text": "Low",
"color": "green",
"index": 0
}
}
},
{
"type": "range",
"options": {
"from": 50,
"to": 80,
"result": {
"text": "Medium",
"color": "orange",
"index": 1
}
}
},
{
"type": "range",
"options": {
"from": 80,
"to": 100,
"result": {
"text": "High",
"color": "red",
"index": 2
}
}
}
]
},
"overrides": [
{
"matcher": {
"id": "byRegexp",
"options": "/^cpu_/"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "decimals",
"value": 2
}
]
}
]
}
}
}
}
},
"panel-3": {
"kind": "Panel",
"spec": {
"id": 3,
"title": "RegexMap Example",
"description": "Panel with RegexMap mapping type - maps values matching regex patterns to colors",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "prometheus",
"version": "v0",
"datasource": {
"name": "prometheus-uid"
},
"spec": {
"expr": "log_level"
}
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "VizConfig",
"group": "stat",
"version": "",
"spec": {
"options": {},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "regex",
"options": {
"pattern": "/^error.*/",
"result": {
"text": "Error",
"color": "red",
"index": 0
}
}
},
{
"type": "regex",
"options": {
"pattern": "/^warn.*/",
"result": {
"text": "Warning",
"color": "orange",
"index": 1
}
}
},
{
"type": "regex",
"options": {
"pattern": "/^info.*/",
"result": {
"text": "Info",
"color": "blue",
"index": 2
}
}
}
]
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "string"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"type": "color-text"
}
}
]
}
]
}
}
}
}
},
"panel-4": {
"kind": "Panel",
"spec": {
"id": 4,
"title": "SpecialValueMap Example",
"description": "Panel with SpecialValueMap mapping type - maps special values like null, NaN, true, false to display text",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "prometheus",
"version": "v0",
"datasource": {
"name": "prometheus-uid"
},
"spec": {
"expr": "some_metric"
}
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "VizConfig",
"group": "stat",
"version": "",
"spec": {
"options": {},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "special",
"options": {
"match": "null",
"result": {
"text": "No Data",
"color": "gray",
"index": 0
}
}
},
{
"type": "special",
"options": {
"match": "nan",
"result": {
"text": "Not a Number",
"color": "gray",
"index": 1
}
}
},
{
"type": "special",
"options": {
"match": "null+nan",
"result": {
"text": "N/A",
"color": "gray",
"index": 2
}
}
},
{
"type": "special",
"options": {
"match": "true",
"result": {
"text": "Yes",
"color": "green",
"index": 3
}
}
},
{
"type": "special",
"options": {
"match": "false",
"result": {
"text": "No",
"color": "red",
"index": 4
}
}
},
{
"type": "special",
"options": {
"match": "empty",
"result": {
"text": "Empty",
"color": "gray",
"index": 5
}
}
}
]
},
"overrides": [
{
"matcher": {
"id": "byFrameRefID",
"options": "A"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "blue",
"mode": "fixed"
}
}
]
}
]
}
}
}
}
},
"panel-6": {
"kind": "Panel",
"spec": {
"id": 6,
"title": "Empty Properties Override Example",
"description": "Panel with override that has empty properties array - tests conversion of overrides without any property modifications",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "prometheus",
"version": "v0",
"datasource": {
"name": "prometheus-uid"
},
"spec": {
"expr": "empty_override_metric"
}
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "VizConfig",
"group": "stat",
"version": "",
"spec": {
"options": {},
"fieldConfig": {
"defaults": {},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "field_with_empty_override"
},
"properties": []
}
]
}
}
}
}
},
"panel-5": {
"kind": "Panel",
"spec": {
"id": 5,
"title": "Combined Mappings and Overrides Example",
"description": "Panel with all mapping types combined - demonstrates mixing different mapping types and multiple override matchers",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "prometheus",
"version": "v0",
"datasource": {
"name": "prometheus-uid"
},
"spec": {
"expr": "combined_metric"
}
},
"refId": "A",
"hidden": false
}
},
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "prometheus",
"version": "v0",
"datasource": {
"name": "prometheus-uid"
},
"spec": {
"expr": "secondary_metric"
}
},
"refId": "B",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "VizConfig",
"group": "table",
"version": "",
"spec": {
"options": {},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "value",
"options": {
"failure": {
"text": "Failure",
"color": "red",
"index": 1
},
"success": {
"text": "Success",
"color": "green",
"index": 0
}
}
},
{
"type": "range",
"options": {
"from": 0,
"to": 100,
"result": {
"text": "In Range",
"color": "blue",
"index": 2
}
}
},
{
"type": "regex",
"options": {
"pattern": "/^[A-Z]{3}-\\d+$/",
"result": {
"text": "ID Format",
"color": "purple",
"index": 3
}
}
},
{
"type": "special",
"options": {
"match": "null",
"result": {
"text": "Missing",
"color": "gray",
"index": 4
}
}
}
]
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "status"
},
"properties": [
{
"id": "custom.width",
"value": 120
},
{
"id": "custom.cellOptions",
"value": {
"type": "color-background"
}
}
]
},
{
"matcher": {
"id": "byRegexp",
"options": "/^value_/"
},
"properties": [
{
"id": "unit",
"value": "short"
},
{
"id": "min",
"value": 0
},
{
"id": "max",
"value": 100
}
]
},
{
"matcher": {
"id": "byType",
"options": "number"
},
"properties": [
{
"id": "decimals",
"value": 2
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "yellow",
"value": 50
},
{
"color": "red",
"value": 80
}
]
}
}
]
},
{
"matcher": {
"id": "byFrameRefID",
"options": "B"
},
"properties": [
{
"id": "displayName",
"value": "Secondary Query"
}
]
},
{
"matcher": {
"id": "byValue",
"options": {
"op": "gte",
"reducer": "allIsNull",
"value": 0
}
},
"properties": [
{
"id": "custom.hidden",
"value": true
}
]
}
]
}
}
}
}
}
},
"layout": {
"kind": "GridLayout",
"spec": {
"items": [
{
"kind": "GridLayoutItem",
"spec": {
"x": 0,
"y": 0,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-1"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 12,
"y": 0,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-2"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 0,
"y": 8,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-3"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 12,
"y": 8,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-4"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 0,
"y": 16,
"width": 24,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-5"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 0,
"y": 24,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-6"
}
}
}
]
}
},
"links": [],
"liveNow": false,
"preload": false,
"tags": [
"value-mapping",
"overrides",
"test"
],
"timeSettings": {
"timezone": "browser",
"from": "now-6h",
"to": "now",
"autoRefresh": "",
"autoRefreshIntervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"hideTimepicker": false,
"fiscalYearStartMonth": 0
},
"title": "Value Mapping and Overrides Test",
"variables": []
},
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -0,0 +1,161 @@
{
"kind": "Dashboard",
"apiVersion": "dashboard.grafana.app/v0alpha1",
"metadata": {
"name": "ad5vfcn",
"namespace": "default",
"uid": "dlMZZl6GndU8gJLUQSmgZxXBPCNXyXhNBeQJhHXl0r4X",
"resourceVersion": "2",
"generation": 2,
"creationTimestamp": "2025-11-28T10:14:21Z",
"labels": {
"grafana.app/deprecatedInternalID": "288"
},
"annotations": {
"grafana.app/createdBy": "user:eex2ofwuj0agwd",
"grafana.app/updatedBy": "user:eex2ofwuj0agwd",
"grafana.app/updatedTimestamp": "2025-11-28T10:15:06Z"
}
},
"spec": {
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 288,
"links": [],
"panels": [
{
"datasource": {
"type": "testdata",
"uid": "gdev-testdata"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.4.0-pre",
"targets": [
{
"datasource": {},
"queryType": "randomWalk"
},
{
"datasource": {},
"queryType": "randomWalk"
}
],
"title": "New panel",
"type": "timeseries"
}
],
"preload": false,
"schemaVersion": 42,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "Panel ds inheritance ",
"uid": "ad5vfcn",
"version": 2
},
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -0,0 +1,227 @@
{
"kind": "Dashboard",
"apiVersion": "dashboard.grafana.app/v2alpha1",
"metadata": {
"name": "ad5vfcn",
"namespace": "default",
"uid": "dlMZZl6GndU8gJLUQSmgZxXBPCNXyXhNBeQJhHXl0r4X",
"resourceVersion": "2",
"generation": 2,
"creationTimestamp": "2025-11-28T10:14:21Z",
"labels": {
"grafana.app/deprecatedInternalID": "288"
},
"annotations": {
"grafana.app/createdBy": "user:eex2ofwuj0agwd",
"grafana.app/updatedBy": "user:eex2ofwuj0agwd",
"grafana.app/updatedTimestamp": "2025-11-28T10:15:06Z"
}
},
"spec": {
"annotations": [
{
"kind": "AnnotationQuery",
"spec": {
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"query": {
"kind": "grafana",
"spec": {}
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"builtIn": true,
"legacyOptions": {
"type": "dashboard"
}
}
}
],
"cursorSync": "Off",
"editable": true,
"elements": {
"panel-1": {
"kind": "Panel",
"spec": {
"id": 1,
"title": "New panel",
"description": "",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "testdata",
"spec": {
"queryType": "randomWalk"
}
},
"datasource": {
"type": "testdata",
"uid": "gdev-testdata"
},
"refId": "A",
"hidden": false
}
},
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "testdata",
"spec": {
"queryType": "randomWalk"
}
},
"datasource": {
"type": "testdata",
"uid": "gdev-testdata"
},
"refId": "B",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "timeseries",
"spec": {
"pluginVersion": "12.4.0-pre",
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"fieldConfig": {
"defaults": {
"thresholds": {
"mode": "absolute",
"steps": [
{
"value": 0,
"color": "green"
},
{
"value": 80,
"color": "red"
}
]
},
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
}
},
"overrides": []
}
}
}
}
}
},
"layout": {
"kind": "GridLayout",
"spec": {
"items": [
{
"kind": "GridLayoutItem",
"spec": {
"x": 0,
"y": 0,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-1"
}
}
}
]
}
},
"links": [],
"liveNow": false,
"preload": false,
"tags": [],
"timeSettings": {
"timezone": "browser",
"from": "now-6h",
"to": "now",
"autoRefresh": "",
"autoRefreshIntervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"hideTimepicker": false,
"fiscalYearStartMonth": 0
},
"title": "Panel ds inheritance ",
"variables": []
},
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -0,0 +1,231 @@
{
"kind": "Dashboard",
"apiVersion": "dashboard.grafana.app/v2beta1",
"metadata": {
"name": "ad5vfcn",
"namespace": "default",
"uid": "dlMZZl6GndU8gJLUQSmgZxXBPCNXyXhNBeQJhHXl0r4X",
"resourceVersion": "2",
"generation": 2,
"creationTimestamp": "2025-11-28T10:14:21Z",
"labels": {
"grafana.app/deprecatedInternalID": "288"
},
"annotations": {
"grafana.app/createdBy": "user:eex2ofwuj0agwd",
"grafana.app/updatedBy": "user:eex2ofwuj0agwd",
"grafana.app/updatedTimestamp": "2025-11-28T10:15:06Z"
}
},
"spec": {
"annotations": [
{
"kind": "AnnotationQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "grafana",
"version": "v0",
"datasource": {
"name": "-- Grafana --"
},
"spec": {}
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"builtIn": true,
"legacyOptions": {
"type": "dashboard"
}
}
}
],
"cursorSync": "Off",
"editable": true,
"elements": {
"panel-1": {
"kind": "Panel",
"spec": {
"id": 1,
"title": "New panel",
"description": "",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "testdata",
"version": "v0",
"datasource": {
"name": "gdev-testdata"
},
"spec": {
"queryType": "randomWalk"
}
},
"refId": "A",
"hidden": false
}
},
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "testdata",
"version": "v0",
"datasource": {
"name": "gdev-testdata"
},
"spec": {
"queryType": "randomWalk"
}
},
"refId": "B",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "VizConfig",
"group": "timeseries",
"version": "12.4.0-pre",
"spec": {
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"fieldConfig": {
"defaults": {
"thresholds": {
"mode": "absolute",
"steps": [
{
"value": 0,
"color": "green"
},
{
"value": 80,
"color": "red"
}
]
},
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
}
},
"overrides": []
}
}
}
}
}
},
"layout": {
"kind": "GridLayout",
"spec": {
"items": [
{
"kind": "GridLayoutItem",
"spec": {
"x": 0,
"y": 0,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-1"
}
}
}
]
}
},
"links": [],
"liveNow": false,
"preload": false,
"tags": [],
"timeSettings": {
"timezone": "browser",
"from": "now-6h",
"to": "now",
"autoRefresh": "",
"autoRefreshIntervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"hideTimepicker": false,
"fiscalYearStartMonth": 0
},
"title": "Panel ds inheritance ",
"variables": []
},
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -0,0 +1,640 @@
{
"kind": "DashboardWithAccessInfo",
"apiVersion": "dashboard.grafana.app/v0alpha1",
"metadata": {
"name": "value-mapping-test",
"namespace": "default",
"uid": "value-mapping-test",
"resourceVersion": "1765384157199094",
"generation": 2,
"creationTimestamp": "2025-11-19T20:09:28Z",
"labels": {
"grafana.app/deprecatedInternalID": "646372978987008"
}
},
"spec": {
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
}
]
},
"description": "Test dashboard for all value mapping types and override matcher types",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"liveNow": false,
"panels": [
{
"description": "Panel with ValueMap mapping type - maps specific text values to colors and display text",
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"critical": {
"color": "red",
"index": 0,
"text": "Critical!"
},
"ok": {
"color": "green",
"index": 2,
"text": "OK"
},
"warning": {
"color": "orange",
"index": 1,
"text": "Warning"
}
},
"type": "value"
}
]
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "status"
},
"properties": [
{
"id": "custom.width",
"value": 100
},
{
"id": "custom.align",
"value": "center"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "up",
"refId": "A"
}
],
"title": "ValueMap Example",
"type": "stat"
},
{
"description": "Panel with RangeMap mapping type - maps numerical ranges to colors and display text",
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"from": 0,
"result": {
"color": "green",
"index": 0,
"text": "Low"
},
"to": 50
},
"type": "range"
},
{
"options": {
"from": 50,
"result": {
"color": "orange",
"index": 1,
"text": "Medium"
},
"to": 80
},
"type": "range"
},
{
"options": {
"from": 80,
"result": {
"color": "red",
"index": 2,
"text": "High"
},
"to": 100
},
"type": "range"
}
]
},
"overrides": [
{
"matcher": {
"id": "byRegexp",
"options": "/^cpu_/"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "decimals",
"value": 2
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "cpu_usage_percent",
"refId": "A"
}
],
"title": "RangeMap Example",
"type": "gauge"
},
{
"description": "Panel with RegexMap mapping type - maps values matching regex patterns to colors",
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"pattern": "/^error.*/",
"result": {
"color": "red",
"index": 0,
"text": "Error"
}
},
"type": "regex"
},
{
"options": {
"pattern": "/^warn.*/",
"result": {
"color": "orange",
"index": 1,
"text": "Warning"
}
},
"type": "regex"
},
{
"options": {
"pattern": "/^info.*/",
"result": {
"color": "blue",
"index": 2,
"text": "Info"
}
},
"type": "regex"
}
]
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "string"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"type": "color-text"
}
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "log_level",
"refId": "A"
}
],
"title": "RegexMap Example",
"type": "stat"
},
{
"description": "Panel with SpecialValueMap mapping type - maps special values like null, NaN, true, false to display text",
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"match": "null",
"result": {
"color": "gray",
"index": 0,
"text": "No Data"
}
},
"type": "special"
},
{
"options": {
"match": "nan",
"result": {
"color": "gray",
"index": 1,
"text": "Not a Number"
}
},
"type": "special"
},
{
"options": {
"match": "null+nan",
"result": {
"color": "gray",
"index": 2,
"text": "N/A"
}
},
"type": "special"
},
{
"options": {
"match": "true",
"result": {
"color": "green",
"index": 3,
"text": "Yes"
}
},
"type": "special"
},
{
"options": {
"match": "false",
"result": {
"color": "red",
"index": 4,
"text": "No"
}
},
"type": "special"
},
{
"options": {
"match": "empty",
"result": {
"color": "gray",
"index": 5,
"text": "Empty"
}
},
"type": "special"
}
]
},
"overrides": [
{
"matcher": {
"id": "byFrameRefID",
"options": "A"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "blue",
"mode": "fixed"
}
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "some_metric",
"refId": "A"
}
],
"title": "SpecialValueMap Example",
"type": "stat"
},
{
"description": "Panel with all mapping types combined - demonstrates mixing different mapping types and multiple override matchers",
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"failure": {
"color": "red",
"index": 1,
"text": "Failure"
},
"success": {
"color": "green",
"index": 0,
"text": "Success"
}
},
"type": "value"
},
{
"options": {
"from": 0,
"result": {
"color": "blue",
"index": 2,
"text": "In Range"
},
"to": 100
},
"type": "range"
},
{
"options": {
"pattern": "/^[A-Z]{3}-\\d+$/",
"result": {
"color": "purple",
"index": 3,
"text": "ID Format"
}
},
"type": "regex"
},
{
"options": {
"match": "null",
"result": {
"color": "gray",
"index": 4,
"text": "Missing"
}
},
"type": "special"
}
]
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "status"
},
"properties": [
{
"id": "custom.width",
"value": 120
},
{
"id": "custom.cellOptions",
"value": {
"type": "color-background"
}
}
]
},
{
"matcher": {
"id": "byRegexp",
"options": "/^value_/"
},
"properties": [
{
"id": "unit",
"value": "short"
},
{
"id": "min",
"value": 0
},
{
"id": "max",
"value": 100
}
]
},
{
"matcher": {
"id": "byType",
"options": "number"
},
"properties": [
{
"id": "decimals",
"value": 2
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "yellow",
"value": 50
},
{
"color": "red",
"value": 80
}
]
}
}
]
},
{
"matcher": {
"id": "byFrameRefID",
"options": "B"
},
"properties": [
{
"id": "displayName",
"value": "Secondary Query"
}
]
},
{
"matcher": {
"id": "byValue",
"options": {
"op": "gte",
"reducer": "allIsNull",
"value": 0
}
},
"properties": [
{
"id": "custom.hidden",
"value": true
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 16
},
"id": 5,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "combined_metric",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "secondary_metric",
"refId": "B"
}
],
"title": "Combined Mappings and Overrides Example",
"type": "table"
},
{
"description": "Panel with override that has empty properties array - tests conversion of overrides without any property modifications",
"fieldConfig": {
"overrides": [
{
"matcher": {
"id": "byName",
"options": "field_with_empty_override"
},
"properties": []
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 6,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "empty_override_metric",
"refId": "A"
}
],
"title": "Empty Properties Override Example",
"type": "stat"
}
],
"preload": false,
"refresh": "",
"schemaVersion": 42,
"tags": [
"value-mapping",
"overrides",
"test"
],
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "browser",
"title": "Value Mapping and Overrides Test"
},
"status": {
"conversion": {
"failed": false,
"storedVersion": "v2beta1"
}
}
}
@@ -0,0 +1,640 @@
{
"kind": "DashboardWithAccessInfo",
"apiVersion": "dashboard.grafana.app/v1beta1",
"metadata": {
"name": "value-mapping-test",
"namespace": "default",
"uid": "value-mapping-test",
"resourceVersion": "1765384157199094",
"generation": 2,
"creationTimestamp": "2025-11-19T20:09:28Z",
"labels": {
"grafana.app/deprecatedInternalID": "646372978987008"
}
},
"spec": {
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"type": "dashboard"
}
]
},
"description": "Test dashboard for all value mapping types and override matcher types",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"liveNow": false,
"panels": [
{
"description": "Panel with ValueMap mapping type - maps specific text values to colors and display text",
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"critical": {
"color": "red",
"index": 0,
"text": "Critical!"
},
"ok": {
"color": "green",
"index": 2,
"text": "OK"
},
"warning": {
"color": "orange",
"index": 1,
"text": "Warning"
}
},
"type": "value"
}
]
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "status"
},
"properties": [
{
"id": "custom.width",
"value": 100
},
{
"id": "custom.align",
"value": "center"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 1,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "up",
"refId": "A"
}
],
"title": "ValueMap Example",
"type": "stat"
},
{
"description": "Panel with RangeMap mapping type - maps numerical ranges to colors and display text",
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"from": 0,
"result": {
"color": "green",
"index": 0,
"text": "Low"
},
"to": 50
},
"type": "range"
},
{
"options": {
"from": 50,
"result": {
"color": "orange",
"index": 1,
"text": "Medium"
},
"to": 80
},
"type": "range"
},
{
"options": {
"from": 80,
"result": {
"color": "red",
"index": 2,
"text": "High"
},
"to": 100
},
"type": "range"
}
]
},
"overrides": [
{
"matcher": {
"id": "byRegexp",
"options": "/^cpu_/"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "decimals",
"value": 2
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "cpu_usage_percent",
"refId": "A"
}
],
"title": "RangeMap Example",
"type": "gauge"
},
{
"description": "Panel with RegexMap mapping type - maps values matching regex patterns to colors",
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"pattern": "/^error.*/",
"result": {
"color": "red",
"index": 0,
"text": "Error"
}
},
"type": "regex"
},
{
"options": {
"pattern": "/^warn.*/",
"result": {
"color": "orange",
"index": 1,
"text": "Warning"
}
},
"type": "regex"
},
{
"options": {
"pattern": "/^info.*/",
"result": {
"color": "blue",
"index": 2,
"text": "Info"
}
},
"type": "regex"
}
]
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "string"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"type": "color-text"
}
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 3,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "log_level",
"refId": "A"
}
],
"title": "RegexMap Example",
"type": "stat"
},
{
"description": "Panel with SpecialValueMap mapping type - maps special values like null, NaN, true, false to display text",
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"match": "null",
"result": {
"color": "gray",
"index": 0,
"text": "No Data"
}
},
"type": "special"
},
{
"options": {
"match": "nan",
"result": {
"color": "gray",
"index": 1,
"text": "Not a Number"
}
},
"type": "special"
},
{
"options": {
"match": "null+nan",
"result": {
"color": "gray",
"index": 2,
"text": "N/A"
}
},
"type": "special"
},
{
"options": {
"match": "true",
"result": {
"color": "green",
"index": 3,
"text": "Yes"
}
},
"type": "special"
},
{
"options": {
"match": "false",
"result": {
"color": "red",
"index": 4,
"text": "No"
}
},
"type": "special"
},
{
"options": {
"match": "empty",
"result": {
"color": "gray",
"index": 5,
"text": "Empty"
}
},
"type": "special"
}
]
},
"overrides": [
{
"matcher": {
"id": "byFrameRefID",
"options": "A"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "blue",
"mode": "fixed"
}
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 4,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "some_metric",
"refId": "A"
}
],
"title": "SpecialValueMap Example",
"type": "stat"
},
{
"description": "Panel with all mapping types combined - demonstrates mixing different mapping types and multiple override matchers",
"fieldConfig": {
"defaults": {
"mappings": [
{
"options": {
"failure": {
"color": "red",
"index": 1,
"text": "Failure"
},
"success": {
"color": "green",
"index": 0,
"text": "Success"
}
},
"type": "value"
},
{
"options": {
"from": 0,
"result": {
"color": "blue",
"index": 2,
"text": "In Range"
},
"to": 100
},
"type": "range"
},
{
"options": {
"pattern": "/^[A-Z]{3}-\\d+$/",
"result": {
"color": "purple",
"index": 3,
"text": "ID Format"
}
},
"type": "regex"
},
{
"options": {
"match": "null",
"result": {
"color": "gray",
"index": 4,
"text": "Missing"
}
},
"type": "special"
}
]
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "status"
},
"properties": [
{
"id": "custom.width",
"value": 120
},
{
"id": "custom.cellOptions",
"value": {
"type": "color-background"
}
}
]
},
{
"matcher": {
"id": "byRegexp",
"options": "/^value_/"
},
"properties": [
{
"id": "unit",
"value": "short"
},
{
"id": "min",
"value": 0
},
{
"id": "max",
"value": 100
}
]
},
{
"matcher": {
"id": "byType",
"options": "number"
},
"properties": [
{
"id": "decimals",
"value": 2
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "yellow",
"value": 50
},
{
"color": "red",
"value": 80
}
]
}
}
]
},
{
"matcher": {
"id": "byFrameRefID",
"options": "B"
},
"properties": [
{
"id": "displayName",
"value": "Secondary Query"
}
]
},
{
"matcher": {
"id": "byValue",
"options": {
"op": "gte",
"reducer": "allIsNull",
"value": 0
}
},
"properties": [
{
"id": "custom.hidden",
"value": true
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 16
},
"id": 5,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "combined_metric",
"refId": "A"
},
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "secondary_metric",
"refId": "B"
}
],
"title": "Combined Mappings and Overrides Example",
"type": "table"
},
{
"description": "Panel with override that has empty properties array - tests conversion of overrides without any property modifications",
"fieldConfig": {
"overrides": [
{
"matcher": {
"id": "byName",
"options": "field_with_empty_override"
},
"properties": []
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 24
},
"id": 6,
"options": {},
"pluginVersion": "",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"expr": "empty_override_metric",
"refId": "A"
}
],
"title": "Empty Properties Override Example",
"type": "stat"
}
],
"preload": false,
"refresh": "",
"schemaVersion": 42,
"tags": [
"value-mapping",
"overrides",
"test"
],
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "browser",
"title": "Value Mapping and Overrides Test"
},
"status": {
"conversion": {
"failed": false,
"storedVersion": "v2beta1"
}
}
}
@@ -0,0 +1,850 @@
{
"kind": "DashboardWithAccessInfo",
"apiVersion": "dashboard.grafana.app/v2alpha1",
"metadata": {
"name": "value-mapping-test",
"namespace": "default",
"uid": "value-mapping-test",
"resourceVersion": "1765384157199094",
"generation": 2,
"creationTimestamp": "2025-11-19T20:09:28Z",
"labels": {
"grafana.app/deprecatedInternalID": "646372978987008"
}
},
"spec": {
"annotations": [
{
"kind": "AnnotationQuery",
"spec": {
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"query": {
"kind": "grafana",
"spec": {}
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"builtIn": true,
"legacyOptions": {
"type": "dashboard"
}
}
}
],
"cursorSync": "Off",
"description": "Test dashboard for all value mapping types and override matcher types",
"editable": true,
"elements": {
"panel-1": {
"kind": "Panel",
"spec": {
"id": 1,
"title": "ValueMap Example",
"description": "Panel with ValueMap mapping type - maps specific text values to colors and display text",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "prometheus",
"spec": {
"expr": "up"
}
},
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "stat",
"spec": {
"pluginVersion": "",
"options": {},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "value",
"options": {
"critical": {
"text": "Critical!",
"color": "red",
"index": 0
},
"ok": {
"text": "OK",
"color": "green",
"index": 2
},
"warning": {
"text": "Warning",
"color": "orange",
"index": 1
}
}
}
]
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "status"
},
"properties": [
{
"id": "custom.width",
"value": 100
},
{
"id": "custom.align",
"value": "center"
}
]
}
]
}
}
}
}
},
"panel-2": {
"kind": "Panel",
"spec": {
"id": 2,
"title": "RangeMap Example",
"description": "Panel with RangeMap mapping type - maps numerical ranges to colors and display text",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "prometheus",
"spec": {
"expr": "cpu_usage_percent"
}
},
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "gauge",
"spec": {
"pluginVersion": "",
"options": {},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "range",
"options": {
"from": 0,
"to": 50,
"result": {
"text": "Low",
"color": "green",
"index": 0
}
}
},
{
"type": "range",
"options": {
"from": 50,
"to": 80,
"result": {
"text": "Medium",
"color": "orange",
"index": 1
}
}
},
{
"type": "range",
"options": {
"from": 80,
"to": 100,
"result": {
"text": "High",
"color": "red",
"index": 2
}
}
}
]
},
"overrides": [
{
"matcher": {
"id": "byRegexp",
"options": "/^cpu_/"
},
"properties": [
{
"id": "unit",
"value": "percent"
},
{
"id": "decimals",
"value": 2
}
]
}
]
}
}
}
}
},
"panel-3": {
"kind": "Panel",
"spec": {
"id": 3,
"title": "RegexMap Example",
"description": "Panel with RegexMap mapping type - maps values matching regex patterns to colors",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "prometheus",
"spec": {
"expr": "log_level"
}
},
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "stat",
"spec": {
"pluginVersion": "",
"options": {},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "regex",
"options": {
"pattern": "/^error.*/",
"result": {
"text": "Error",
"color": "red",
"index": 0
}
}
},
{
"type": "regex",
"options": {
"pattern": "/^warn.*/",
"result": {
"text": "Warning",
"color": "orange",
"index": 1
}
}
},
{
"type": "regex",
"options": {
"pattern": "/^info.*/",
"result": {
"text": "Info",
"color": "blue",
"index": 2
}
}
}
]
},
"overrides": [
{
"matcher": {
"id": "byType",
"options": "string"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"type": "color-text"
}
}
]
}
]
}
}
}
}
},
"panel-4": {
"kind": "Panel",
"spec": {
"id": 4,
"title": "SpecialValueMap Example",
"description": "Panel with SpecialValueMap mapping type - maps special values like null, NaN, true, false to display text",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "prometheus",
"spec": {
"expr": "some_metric"
}
},
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "stat",
"spec": {
"pluginVersion": "",
"options": {},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "special",
"options": {
"match": "null",
"result": {
"text": "No Data",
"color": "gray",
"index": 0
}
}
},
{
"type": "special",
"options": {
"match": "nan",
"result": {
"text": "Not a Number",
"color": "gray",
"index": 1
}
}
},
{
"type": "special",
"options": {
"match": "null+nan",
"result": {
"text": "N/A",
"color": "gray",
"index": 2
}
}
},
{
"type": "special",
"options": {
"match": "true",
"result": {
"text": "Yes",
"color": "green",
"index": 3
}
}
},
{
"type": "special",
"options": {
"match": "false",
"result": {
"text": "No",
"color": "red",
"index": 4
}
}
},
{
"type": "special",
"options": {
"match": "empty",
"result": {
"text": "Empty",
"color": "gray",
"index": 5
}
}
}
]
},
"overrides": [
{
"matcher": {
"id": "byFrameRefID",
"options": "A"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "blue",
"mode": "fixed"
}
}
]
}
]
}
}
}
}
},
"panel-5": {
"kind": "Panel",
"spec": {
"id": 5,
"title": "Combined Mappings and Overrides Example",
"description": "Panel with all mapping types combined - demonstrates mixing different mapping types and multiple override matchers",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "prometheus",
"spec": {
"expr": "combined_metric"
}
},
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"refId": "A",
"hidden": false
}
},
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "prometheus",
"spec": {
"expr": "secondary_metric"
}
},
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"refId": "B",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "table",
"spec": {
"pluginVersion": "",
"options": {},
"fieldConfig": {
"defaults": {
"mappings": [
{
"type": "value",
"options": {
"failure": {
"text": "Failure",
"color": "red",
"index": 1
},
"success": {
"text": "Success",
"color": "green",
"index": 0
}
}
},
{
"type": "range",
"options": {
"from": 0,
"to": 100,
"result": {
"text": "In Range",
"color": "blue",
"index": 2
}
}
},
{
"type": "regex",
"options": {
"pattern": "/^[A-Z]{3}-\\d+$/",
"result": {
"text": "ID Format",
"color": "purple",
"index": 3
}
}
},
{
"type": "special",
"options": {
"match": "null",
"result": {
"text": "Missing",
"color": "gray",
"index": 4
}
}
}
]
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "status"
},
"properties": [
{
"id": "custom.width",
"value": 120
},
{
"id": "custom.cellOptions",
"value": {
"type": "color-background"
}
}
]
},
{
"matcher": {
"id": "byRegexp",
"options": "/^value_/"
},
"properties": [
{
"id": "unit",
"value": "short"
},
{
"id": "min",
"value": 0
},
{
"id": "max",
"value": 100
}
]
},
{
"matcher": {
"id": "byType",
"options": "number"
},
"properties": [
{
"id": "decimals",
"value": 2
},
{
"id": "thresholds",
"value": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "yellow",
"value": 50
},
{
"color": "red",
"value": 80
}
]
}
}
]
},
{
"matcher": {
"id": "byFrameRefID",
"options": "B"
},
"properties": [
{
"id": "displayName",
"value": "Secondary Query"
}
]
},
{
"matcher": {
"id": "byValue",
"options": {
"op": "gte",
"reducer": "allIsNull",
"value": 0
}
},
"properties": [
{
"id": "custom.hidden",
"value": true
}
]
}
]
}
}
}
}
},
"panel-6": {
"kind": "Panel",
"spec": {
"id": 6,
"title": "Empty Properties Override Example",
"description": "Panel with override that has empty properties array - tests conversion of overrides without any property modifications",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "prometheus",
"spec": {
"expr": "empty_override_metric"
}
},
"datasource": {
"type": "prometheus",
"uid": "prometheus-uid"
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "stat",
"spec": {
"pluginVersion": "",
"options": {},
"fieldConfig": {
"defaults": {},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "field_with_empty_override"
},
"properties": []
}
]
}
}
}
}
}
},
"layout": {
"kind": "GridLayout",
"spec": {
"items": [
{
"kind": "GridLayoutItem",
"spec": {
"x": 0,
"y": 0,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-1"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 12,
"y": 0,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-2"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 0,
"y": 8,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-3"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 12,
"y": 8,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-4"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 0,
"y": 16,
"width": 24,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-5"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 0,
"y": 24,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-6"
}
}
}
]
}
},
"links": [],
"liveNow": false,
"preload": false,
"tags": [
"value-mapping",
"overrides",
"test"
],
"timeSettings": {
"timezone": "browser",
"from": "now-6h",
"to": "now",
"autoRefresh": "",
"autoRefreshIntervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"hideTimepicker": false,
"fiscalYearStartMonth": 0
},
"title": "Value Mapping and Overrides Test",
"variables": []
},
"status": {
"conversion": {
"failed": false,
"storedVersion": "v2beta1"
}
}
}
@@ -2006,6 +2006,28 @@ func transformPanelQueries(ctx context.Context, panelMap map[string]interface{},
}
}
// Ensure each target has a non-empty refId. We only fill missing refIds;
existingRefIds := make(map[string]bool)
for _, target := range targets {
if targetMap, ok := target.(map[string]interface{}); ok {
if refId := schemaversion.GetStringValue(targetMap, "refId"); refId != "" {
existingRefIds[refId] = true
}
}
}
for _, target := range targets {
targetMap, ok := target.(map[string]interface{})
if !ok {
continue
}
refId := schemaversion.GetStringValue(targetMap, "refId")
if refId == "" {
refId = nextAvailableRefId(existingRefIds)
targetMap["refId"] = refId
existingRefIds[refId] = true
}
}
queries := make([]dashv2alpha1.DashboardPanelQueryKind, 0, len(targets))
for _, target := range targets {
@@ -2018,6 +2040,27 @@ func transformPanelQueries(ctx context.Context, panelMap map[string]interface{},
return queries
}
// nextAvailableRefId returns the next unused refId using the same sequence as the
// frontend helper (A, B, ..., Z, AA, AB, ...).
func nextAvailableRefId(existing map[string]bool) string {
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
var refIdFromIndex func(num int) string
refIdFromIndex = func(num int) string {
if num < len(letters) {
return string(letters[num])
}
return refIdFromIndex(num/len(letters)-1) + string(letters[num%len(letters)])
}
for i := 0; ; i++ {
refId := refIdFromIndex(i)
if !existing[refId] {
return refId
}
}
}
func transformSingleQuery(ctx context.Context, targetMap map[string]interface{}, panelDatasource *dashv2alpha1.DashboardDataSourceRef, dsIndexProvider schemaversion.DataSourceIndexProvider) dashv2alpha1.DashboardPanelQueryKind {
refId := schemaversion.GetStringValue(targetMap, "refId", "A")
if refId == "" {
@@ -210,6 +210,48 @@ func TestV1beta1ToV2alpha1(t *testing.T) {
assert.Equal(t, "", query.Spec.Query.Kind, "Query kind should be empty when datasource is empty object {}")
},
},
{
name: "missing refIds are assigned while existing refIds are preserved",
createV1beta1: func() *dashv1.Dashboard {
return &dashv1.Dashboard{
Spec: dashv1.DashboardSpec{
Object: map[string]interface{}{
"title": "Test Dashboard",
"panels": []interface{}{
map[string]interface{}{
"id": 1,
"type": "bargauge",
"targets": []interface{}{
map[string]interface{}{
"refId": "",
"scenarioId": "random_walk",
},
map[string]interface{}{
"refId": "A",
"scenarioId": "random_walk",
},
map[string]interface{}{
"refId": "",
"scenarioId": "random_walk",
},
},
},
},
},
},
}
},
validateV2alpha1: func(t *testing.T, v2alpha1 *dashv2alpha1.Dashboard) {
require.NotNil(t, v2alpha1.Spec.Elements["panel-1"])
panel := v2alpha1.Spec.Elements["panel-1"].PanelKind
require.NotNil(t, panel)
require.Len(t, panel.Spec.Data.Spec.Queries, 3)
assert.Equal(t, "B", panel.Spec.Data.Spec.Queries[0].Spec.RefId)
assert.Equal(t, "A", panel.Spec.Data.Spec.Queries[1].Spec.RefId)
assert.Equal(t, "C", panel.Spec.Data.Spec.Queries[2].Spec.RefId)
},
},
}
for _, tt := range testCases {
@@ -1973,16 +1973,16 @@ func convertFieldConfigOverridesToV1(overrides []dashv2alpha1.DashboardV2alpha1F
"options": override.Matcher.Options,
}
properties := make([]map[string]interface{}, 0, len(override.Properties))
if len(override.Properties) > 0 {
properties := make([]map[string]interface{}, 0, len(override.Properties))
for _, prop := range override.Properties {
properties = append(properties, map[string]interface{}{
"id": prop.Id,
"value": prop.Value,
})
}
overrideMap["properties"] = properties
}
overrideMap["properties"] = properties
result = append(result, overrideMap)
}
@@ -2074,11 +2074,9 @@ func convertRegexMapToV1(regexMap *dashv2alpha1.DashboardRegexMap) map[string]in
return nil
}
options := []map[string]interface{}{
{
"pattern": regexMap.Options.Pattern,
"result": convertValueMappingResultToV1(regexMap.Options.Result),
},
options := map[string]interface{}{
"pattern": regexMap.Options.Pattern,
"result": convertValueMappingResultToV1(regexMap.Options.Result),
}
return map[string]interface{}{
@@ -7,7 +7,15 @@
MYSQL_PASSWORD: password
ports:
- "3306:3306"
command: [mysqld, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --innodb_monitor_enable=all, --default-authentication-plugin=mysql_native_password]
command:
- mysqld
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --innodb_monitor_enable=all
- --default-authentication-plugin=mysql_native_password
# Please keep sql-require-primary-key option enabled, to make sure we don't accidentally introduce migration
# adding new table without PK.
- --sql-require-primary-key=ON
fake-mysql-data:
image: grafana/fake-data-gen
@@ -46,7 +46,7 @@ Complete the following steps to install Grafana from the APT repository:
1. Install the prerequisite packages:
```bash
sudo apt-get install -y apt-transport-https software-properties-common wget
sudo apt-get install -y apt-transport-https wget
```
1. Import the GPG key:
+4
View File
@@ -828,6 +828,10 @@ export interface FeatureToggles {
*/
fetchRulesUsingPost?: boolean;
/**
* Add compact=true when fetching rules
*/
fetchRulesInCompactMode?: boolean;
/**
* Enables the new logs panel
* @default true
*/
@@ -9,7 +9,7 @@ const Components = resolveSelectors(versionedComponents);
const selectors = { pages: Pages, components: Components };
/**
* Exposes Pages, Component selectors and E2ESelectors type in package for easy use in e2e tests and in production code
* Exposes Pages, Component selectors, and E2ESelectors type in package for easy use in e2e tests and in production code.
*/
export {
Pages,
@@ -15,7 +15,7 @@ export type FunctionSelector = (id: string) => string;
export type FunctionSelectorTwoArgs = (arg1: string, arg2: string) => string;
/**
* A function selector without argument
* A function selector without arguments
*/
export type CssSelector = () => string;
@@ -360,7 +360,17 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resourcepb.Reso
})
}
list.TotalHits = int64(len(list.Results.Rows))
// the UI expects us to populate "TotalHits" with the total search hits, not however many we are returning.
// this is a dumb workaround due to the fact that legacy doesn't expose a way of "counting search hits"
// it fixes a bug that only happens in mode 1-2 that prevents pagination from working in the dashboard list view
// we only have a handful of instances running in this mode and moving towards 0 instances fast, so this is fine
query.Limit = 0
query.Page = 1
res, err = c.dashboardStore.FindDashboards(ctx, query)
if err != nil {
return nil, err
}
list.TotalHits = int64(len(res))
return list, nil
}
@@ -48,6 +48,18 @@ func TestDashboardSearchClient_Search(t *testing.T) {
{ID: 2, UID: "uid2", Title: "Test Dashboard2", FolderUID: "folder2", Tags: []string{}},
}, nil).Once()
// Second call for total count with Page: 1, Limit: 0
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
SignedInUser: user,
Type: "dash-db",
Sort: sorter,
Limit: 0,
Page: 1,
}).Return([]dashboards.DashboardSearchProjection{
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1", Tags: []string{"term"}},
{ID: 2, UID: "uid2", Title: "Test Dashboard2", FolderUID: "folder2", Tags: []string{}},
}, nil).Once()
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: dashboardKey,
@@ -124,6 +136,17 @@ func TestDashboardSearchClient_Search(t *testing.T) {
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(50), Tags: []string{}},
}, nil).Once()
// Second call for total count with Page: 1, Limit: 0
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
SignedInUser: user,
Type: "dash-db",
Sort: sortOptionAsc,
Limit: 0,
Page: 1,
}).Return([]dashboards.DashboardSearchProjection{
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(50), Tags: []string{}},
}, nil).Once()
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: dashboardKey,
@@ -189,6 +212,17 @@ func TestDashboardSearchClient_Search(t *testing.T) {
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(2), Tags: []string{}},
}, nil).Once()
// Second call for total count with Page: 1, Limit: 0
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
SignedInUser: user,
Type: "dash-db",
Sort: sortOptionAsc,
Limit: 0,
Page: 1,
}).Return([]dashboards.DashboardSearchProjection{
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder", SortMeta: int64(2), Tags: []string{}},
}, nil).Once()
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: dashboardKey,
@@ -293,6 +327,17 @@ func TestDashboardSearchClient_Search(t *testing.T) {
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
}, nil).Once()
// Second call for total count with Page: 1, Limit: 0
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
Title: "test",
SignedInUser: user,
Type: "dash-db",
Limit: 0,
Page: 1,
}).Return([]dashboards.DashboardSearchProjection{
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
}, nil).Once()
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: dashboardKey,
@@ -319,6 +364,18 @@ func TestDashboardSearchClient_Search(t *testing.T) {
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
}, nil).Once()
// Second call for total count with Page: 1, Limit: 0
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
Title: "test",
TitleExactMatch: true,
SignedInUser: user,
Type: "dash-db",
Limit: 0,
Page: 1,
}).Return([]dashboards.DashboardSearchProjection{
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
}, nil).Once()
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: dashboardKey,
@@ -350,6 +407,17 @@ func TestDashboardSearchClient_Search(t *testing.T) {
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
}, nil).Once()
// Second call for total count with Page: 1, Limit: 0
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
DashboardIds: []int64{1, 2},
SignedInUser: user,
Type: "dash-db",
Limit: 0,
Page: 1,
}).Return([]dashboards.DashboardSearchProjection{
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
}, nil).Once()
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: dashboardKey,
@@ -383,6 +451,19 @@ func TestDashboardSearchClient_Search(t *testing.T) {
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
}, nil).Once()
// Second call for total count with Page: 1, Limit: 0
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
DashboardUIDs: []string{"uid1", "uid2"},
Tags: []string{"tag1", "tag2"},
FolderUIDs: []string{"general", "folder1"},
SignedInUser: user,
Type: "dash-db",
Limit: 0,
Page: 1,
}).Return([]dashboards.DashboardSearchProjection{
{UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
}, nil).Once()
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: dashboardKey,
@@ -532,6 +613,17 @@ func TestDashboardSearchClient_Search(t *testing.T) {
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
}, nil).Once()
// Second call for total count with Page: 1, Limit: 0
mockStore.On("FindDashboards", mock.Anything, &dashboards.FindPersistedDashboardsQuery{
SignedInUser: user,
Sort: sort.SortAlphaAsc,
Type: "dash-db",
Limit: 0,
Page: 1,
}).Return([]dashboards.DashboardSearchProjection{
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1"},
}, nil).Once()
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
Key: dashboardKey,
@@ -552,7 +644,7 @@ func TestDashboardSearchClient_Search(t *testing.T) {
t.Run("Should set correct sort field when sorting by views", func(t *testing.T) {
mockStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{
{ID: 1, UID: "uid", Title: "Test Dashboard", FolderUID: "folder1", SortMeta: 100},
}, nil).Once()
}, nil).Twice() // Will be called twice due to the total count call
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
@@ -584,7 +676,7 @@ func TestDashboardSearchClient_Search(t *testing.T) {
mockStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{
{UID: "dashboard1", FolderUID: "folder1", ID: 1},
{UID: "dashboard2", FolderUID: "folder2", ID: 2},
}, nil).Once()
}, nil).Twice() // Will be called twice due to the total count call
req := &resourcepb.ResourceSearchRequest{
Options: &resourcepb.ListOptions{
+79
View File
@@ -0,0 +1,79 @@
package iam
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/server"
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/registry/apis/iam/noopstorage"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
)
// ApiInstaller is a generic interface for installing API resources.
// This allows different implementations (OSS noop, Enterprise with features)
// to provide their own resource registration and validation logic.
type ApiInstaller[T runtime.Object] interface {
// GetAuthorizer returns the authorizer for the API group.
GetAuthorizer() authorizer.Authorizer
// RegisterStorage registers storage and hooks with the API group.
RegisterStorage(apiGroupInfo *server.APIGroupInfo, opts *builder.APIGroupOptions, storage map[string]rest.Storage) error
// ValidateOnCreate validates object creation.
ValidateOnCreate(ctx context.Context, obj T) error
// ValidateOnUpdate validates object updates.
ValidateOnUpdate(ctx context.Context, oldObj, newObj T) error
// ValidateOnDelete validates object deletion.
ValidateOnDelete(ctx context.Context, obj T) error
}
// RoleApiInstaller provides Role-specific API registration and validation.
// This interface allows enterprise implementations to provide custom role handling
// while keeping the core IAM registration logic in OSS.
type RoleApiInstaller ApiInstaller[*iamv0.Role]
// NoopApiInstaller is a no-op implementation for when roles are not available (OSS).
type NoopApiInstaller[T runtime.Object] struct {
ResourceInfo utils.ResourceInfo
}
func (n *NoopApiInstaller[T]) GetAuthorizer() authorizer.Authorizer {
return authorizer.AuthorizerFunc(func(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) {
return authorizer.DecisionDeny, "Unavailable functionality", nil
})
}
func (n *NoopApiInstaller[T]) RegisterStorage(apiGroupInfo *server.APIGroupInfo, opts *builder.APIGroupOptions, storage map[string]rest.Storage) error {
storage[n.ResourceInfo.StoragePath()] = &noopstorage.NoopREST{ResourceInfo: n.ResourceInfo}
return nil
}
func (n *NoopApiInstaller[T]) ValidateOnCreate(ctx context.Context, obj T) error {
// No validation needed in OSS
return nil
}
func (n *NoopApiInstaller[T]) ValidateOnUpdate(ctx context.Context, oldObj, newObj T) error {
// No validation needed in OSS
return nil
}
func (n *NoopApiInstaller[T]) ValidateOnDelete(ctx context.Context, obj T) error {
// No validation needed in OSS
return nil
}
// ProvideNoopRoleApiInstaller provides a no-op role installer specifically for Role types.
// This is needed for Wire dependency injection which doesn't handle generic functions well.
func ProvideNoopRoleApiInstaller() RoleApiInstaller {
return &NoopApiInstaller[*iamv0.Role]{
ResourceInfo: iamv0.RoleInfo,
}
}
+6 -2
View File
@@ -19,7 +19,11 @@ type iamAuthorizer struct {
resourceAuthorizer map[string]authorizer.Authorizer // Map resource to its authorizer
}
func newIAMAuthorizer(accessClient authlib.AccessClient, legacyAccessClient authlib.AccessClient) authorizer.Authorizer {
func newIAMAuthorizer(
accessClient authlib.AccessClient,
legacyAccessClient authlib.AccessClient,
roleApiInstaller RoleApiInstaller,
) authorizer.Authorizer {
resourceAuthorizer := make(map[string]authorizer.Authorizer)
serviceAuthorizer := gfauthorizer.NewServiceAuthorizer()
@@ -44,7 +48,7 @@ func newIAMAuthorizer(accessClient authlib.AccessClient, legacyAccessClient auth
// Access specific resources
authorizer := gfauthorizer.NewResourceAuthorizer(accessClient)
resourceAuthorizer[iamv0.CoreRoleInfo.GetName()] = iamauthorizer.NewCoreRoleAuthorizer(accessClient)
resourceAuthorizer[iamv0.RoleInfo.GetName()] = authorizer
resourceAuthorizer[iamv0.RoleInfo.GetName()] = roleApiInstaller.GetAuthorizer()
resourceAuthorizer[iamv0.ResourcePermissionInfo.GetName()] = allowAuthorizer // Handled by the backend wrapper
resourceAuthorizer[iamv0.RoleBindingInfo.GetName()] = authorizer
resourceAuthorizer[iamv0.ServiceAccountResourceInfo.GetName()] = authorizer
+1 -1
View File
@@ -56,7 +56,7 @@ type IdentityAccessManagementAPIBuilder struct {
teamBindingLegacyStore *teambinding.LegacyBindingStore
ssoLegacyStore *sso.LegacyStore
coreRolesStorage CoreRoleStorageBackend
rolesStorage RoleStorageBackend
roleApiInstaller RoleApiInstaller
resourcePermissionsStorage resource.StorageBackend
roleBindingsStorage RoleBindingStorageBackend
externalGroupMappingStorage ExternalGroupMappingStorageBackend
+74
View File
@@ -0,0 +1,74 @@
package noopstorage
import (
"context"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apiserver/pkg/registry/rest"
"github.com/grafana/grafana/pkg/apimachinery/utils"
)
type NoopREST struct {
ResourceInfo utils.ResourceInfo
}
var (
_ rest.Storage = (*NoopREST)(nil)
_ rest.Scoper = (*NoopREST)(nil)
_ rest.SingularNameProvider = (*NoopREST)(nil)
_ rest.Getter = (*NoopREST)(nil)
)
func (n *NoopREST) New() runtime.Object {
return n.ResourceInfo.NewFunc()
}
func (n *NoopREST) NewList() runtime.Object {
return n.ResourceInfo.NewListFunc()
}
func (n *NoopREST) NamespaceScoped() bool {
return true
}
func (n *NoopREST) GetSingularName() string {
return n.ResourceInfo.GetSingularName()
}
func (n *NoopREST) Destroy() {}
func (n *NoopREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return nil, errNoopStorage
}
func (n *NoopREST) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
return nil, errNoopStorage
}
func (n *NoopREST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
return nil, errNoopStorage
}
func (n *NoopREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
return nil, false, errNoopStorage
}
func (n *NoopREST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
return nil, false, errNoopStorage
}
func (n *NoopREST) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *internalversion.ListOptions) (runtime.Object, error) {
return nil, errNoopStorage
}
func (n *NoopREST) Watch(ctx context.Context, options *internalversion.ListOptions) (watch.Interface, error) {
return nil, errNoopStorage
}
func (n *NoopREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
return nil, errNoopStorage
}
+23 -17
View File
@@ -72,7 +72,7 @@ func RegisterAPIService(
zClient zanzana.Client,
reg prometheus.Registerer,
coreRolesStorage CoreRoleStorageBackend,
rolesStorage RoleStorageBackend,
roleApiInstaller RoleApiInstaller,
tracing *tracing.TracingService,
roleBindingsStorage RoleBindingStorageBackend,
externalGroupMappingStorageBackend ExternalGroupMappingStorageBackend,
@@ -87,7 +87,7 @@ func RegisterAPIService(
dbProvider := legacysql.NewDatabaseProvider(sql)
store := legacy.NewLegacySQLStores(dbProvider)
legacyAccessClient := newLegacyAccessClient(ac, store)
authorizer := newIAMAuthorizer(accessClient, legacyAccessClient)
authorizer := newIAMAuthorizer(accessClient, legacyAccessClient, roleApiInstaller)
registerMetrics(reg)
//nolint:staticcheck // not yet migrated to OpenFeature
@@ -106,7 +106,7 @@ func RegisterAPIService(
teamBindingLegacyStore: teambinding.NewLegacyBindingStore(store, enableAuthnMutation, tracing),
ssoLegacyStore: sso.NewLegacyStore(ssoService, tracing),
coreRolesStorage: coreRolesStorage,
rolesStorage: rolesStorage,
roleApiInstaller: roleApiInstaller,
resourcePermissionsStorage: resourcepermission.ProvideStorageBackend(dbProvider),
roleBindingsStorage: roleBindingsStorage,
externalGroupMappingStorage: externalGroupMappingStorageBackend,
@@ -139,7 +139,7 @@ func NewAPIService(
accessClient types.AccessClient,
dbProvider legacysql.LegacyDatabaseProvider,
coreRoleStorage CoreRoleStorageBackend,
roleStorage RoleStorageBackend,
roleApiInstaller RoleApiInstaller,
features featuremgmt.FeatureToggles,
zClient zanzana.Client,
reg prometheus.Registerer,
@@ -162,7 +162,6 @@ func NewAPIService(
store: store,
display: user.NewLegacyDisplayREST(store),
resourcePermissionsStorage: resourcePermissionsStorage,
rolesStorage: roleStorage,
coreRolesStorage: coreRoleStorage,
roleBindingsStorage: noopstorage.ProvideStorageBackend(), // TODO: add a proper storage backend
logger: log.New("iam.apis"),
@@ -172,6 +171,7 @@ func NewAPIService(
zClient: zClient,
zTickets: make(chan bool, MaxConcurrentZanzanaWrites),
reg: reg,
roleApiInstaller: roleApiInstaller,
authorizer: authorizer.AuthorizerFunc(
func(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
user, ok := types.AuthInfoFrom(ctx)
@@ -377,23 +377,17 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *ge
}
if enableZanzanaSync {
b.logger.Info("Enabling hooks for CoreRole to sync to Zanzana")
coreRoleStore.AfterCreate = b.AfterRoleCreate
coreRoleStore.AfterDelete = b.AfterRoleDelete
coreRoleStore.BeginUpdate = b.BeginRoleUpdate
h := NewRoleHooks(b.zClient, b.zTickets, b.logger)
coreRoleStore.AfterCreate = h.AfterRoleCreate
coreRoleStore.AfterDelete = h.AfterRoleDelete
coreRoleStore.BeginUpdate = h.BeginRoleUpdate
}
storage[iamv0.CoreRoleInfo.StoragePath()] = coreRoleStore
roleStore, err := NewLocalStore(iamv0.RoleInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.rolesStorage)
if err != nil {
// Role registration is delegated to the RoleApiInstaller
if err := b.roleApiInstaller.RegisterStorage(apiGroupInfo, &opts, storage); err != nil {
return err
}
if enableZanzanaSync {
b.logger.Info("Enabling hooks for Role to sync to Zanzana")
roleStore.AfterCreate = b.AfterRoleCreate
roleStore.AfterDelete = b.AfterRoleDelete
roleStore.BeginUpdate = b.BeginRoleUpdate
}
storage[iamv0.RoleInfo.StoragePath()] = roleStore
roleBindingStore, err := NewLocalStore(iamv0.RoleBindingInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.roleBindingsStorage)
if err != nil {
@@ -568,6 +562,8 @@ func (b *IdentityAccessManagementAPIBuilder) Validate(ctx context.Context, a adm
return resourcepermission.ValidateCreateAndUpdateInput(ctx, typedObj)
case *iamv0.ExternalGroupMapping:
return externalgroupmapping.ValidateOnCreate(typedObj)
case *iamv0.Role:
return b.roleApiInstaller.ValidateOnCreate(ctx, typedObj)
}
return nil
case admission.Update:
@@ -592,9 +588,19 @@ func (b *IdentityAccessManagementAPIBuilder) Validate(ctx context.Context, a adm
return fmt.Errorf("expected old object to be a TeamBinding, got %T", oldTeamBindingObj)
}
return teambinding.ValidateOnUpdate(ctx, typedObj, oldTeamBindingObj)
case *iamv0.Role:
oldRoleObj, ok := a.GetOldObject().(*iamv0.Role)
if !ok {
return fmt.Errorf("expected old object to be a Role, got %T", oldRoleObj)
}
return b.roleApiInstaller.ValidateOnUpdate(ctx, oldRoleObj, typedObj)
}
return nil
case admission.Delete:
switch oldRoleObj := a.GetOldObject().(type) {
case *iamv0.Role:
return b.roleApiInstaller.ValidateOnDelete(ctx, oldRoleObj)
}
return nil
case admission.Connect:
return nil
+41 -30
View File
@@ -9,12 +9,23 @@ import (
"k8s.io/apiserver/pkg/registry/generic/registry"
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol"
v1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
"github.com/grafana/grafana/pkg/services/authz/zanzana"
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
)
type RoleHooks struct {
zClient zanzana.Client
zTickets chan bool
logger log.Logger
}
func NewRoleHooks(zClient zanzana.Client, zTickets chan bool, logger log.Logger) *RoleHooks {
return &RoleHooks{zClient: zClient, zTickets: zTickets, logger: logger}
}
// convertRolePermissionsToTuples converts role permissions (action/scope) to v1 TupleKey format
// using the shared zanzana.ConvertRolePermissionsToTuples utility and common.ToAuthzExtTupleKeys
func convertRolePermissionsToTuples(roleUID string, permissions []iamv0.CoreRolespecPermission) ([]*v1.TupleKey, error) {
@@ -44,8 +55,8 @@ func convertRolePermissionsToTuples(roleUID string, permissions []iamv0.CoreRole
// AfterRoleCreate is a post-create hook that writes the role permissions to Zanzana (openFGA)
// It handles both Role and CoreRole types
func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object, _ *metav1.CreateOptions) {
if b.zClient == nil {
func (h *RoleHooks) AfterRoleCreate(obj runtime.Object, _ *metav1.CreateOptions) {
if h.zClient == nil {
return
}
@@ -76,21 +87,21 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object,
}
wait := time.Now()
b.zTickets <- true
h.zTickets <- true
hooksWaitHistogram.WithLabelValues(rType, "create").Observe(time.Since(wait).Seconds())
go func(role *iamv0.CoreRole, roleType string) {
start := time.Now()
status := "success"
defer func() {
<-b.zTickets
<-h.zTickets
hooksDurationHistogram.WithLabelValues(rType, "create", status).Observe(time.Since(start).Seconds())
hooksOperationCounter.WithLabelValues(rType, "create", status).Inc()
}()
tuples, err := convertRolePermissionsToTuples(role.Name, role.Spec.Permissions)
if err != nil {
b.logger.Error("failed to convert role permissions to tuples",
h.logger.Error("failed to convert role permissions to tuples",
"namespace", role.Namespace,
"roleUID", role.Name,
"roleType", roleType,
@@ -103,7 +114,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object,
// Avoid writing if there are no valid tuples
if len(tuples) == 0 {
b.logger.Debug("no valid tuples to write for role",
h.logger.Debug("no valid tuples to write for role",
"namespace", role.Namespace,
"roleUID", role.Name,
"roleType", roleType,
@@ -113,7 +124,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object,
return
}
b.logger.Debug("writing role permissions to zanzana",
h.logger.Debug("writing role permissions to zanzana",
"namespace", role.Namespace,
"roleUID", role.Name,
"roleType", roleType,
@@ -124,14 +135,14 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object,
ctx, cancel := context.WithTimeout(context.Background(), defaultWriteTimeout)
defer cancel()
err = b.zClient.Write(ctx, &v1.WriteRequest{
err = h.zClient.Write(ctx, &v1.WriteRequest{
Namespace: role.Namespace,
Writes: &v1.WriteRequestWrites{
TupleKeys: tuples,
},
})
if err != nil {
b.logger.Error("failed to write role permissions to zanzana",
h.logger.Error("failed to write role permissions to zanzana",
"err", err,
"namespace", role.Namespace,
"roleUID", role.Name,
@@ -149,8 +160,8 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object,
// AfterRoleDelete is a post-delete hook that removes the role permissions from Zanzana (openFGA)
// It handles both Role and CoreRole types
func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object, _ *metav1.DeleteOptions) {
if b.zClient == nil {
func (h *RoleHooks) AfterRoleDelete(obj runtime.Object, _ *metav1.DeleteOptions) {
if h.zClient == nil {
return
}
@@ -182,15 +193,15 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
}
wait := time.Now()
b.zTickets <- true
h.zTickets <- true
hooksWaitHistogram.WithLabelValues("role", "delete").Observe(time.Since(wait).Seconds()) // Record wait time
go func(role *iamv0.CoreRole, roleType string) {
defer func() {
<-b.zTickets
<-h.zTickets
}()
b.logger.Debug("deleting role permissions from zanzana",
h.logger.Debug("deleting role permissions from zanzana",
"namespace", role.Namespace,
"roleUID", role.Name,
"roleType", roleType,
@@ -199,7 +210,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
tuples, err := convertRolePermissionsToTuples(role.Name, role.Spec.Permissions)
if err != nil {
b.logger.Error("failed to convert role permissions to tuples for deletion",
h.logger.Error("failed to convert role permissions to tuples for deletion",
"namespace", role.Namespace,
"roleUID", role.Name,
"roleType", roleType,
@@ -211,7 +222,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
// Avoid deleting if there are no valid tuples
if len(tuples) == 0 {
b.logger.Debug("no valid tuples to delete for role",
h.logger.Debug("no valid tuples to delete for role",
"namespace", role.Namespace,
"roleUID", role.Name,
"roleType", roleType,
@@ -223,7 +234,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
// Convert tuples to TupleKeyWithoutCondition for deletion
deleteTuples := toTupleKeysWithoutCondition(tuples)
b.logger.Debug("deleting role permissions from zanzana",
h.logger.Debug("deleting role permissions from zanzana",
"namespace", role.Namespace,
"roleUID", role.Name,
"roleType", roleType,
@@ -234,14 +245,14 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
ctx, cancel := context.WithTimeout(context.Background(), defaultWriteTimeout)
defer cancel()
err = b.zClient.Write(ctx, &v1.WriteRequest{
err = h.zClient.Write(ctx, &v1.WriteRequest{
Namespace: role.Namespace,
Deletes: &v1.WriteRequestDeletes{
TupleKeys: deleteTuples,
},
})
if err != nil {
b.logger.Error("failed to delete role permissions from zanzana",
h.logger.Error("failed to delete role permissions from zanzana",
"err", err,
"namespace", role.Namespace,
"roleUID", role.Name,
@@ -255,8 +266,8 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
// beginRoleUpdate is a pre-update hook that prepares zanzana updates
// It converts old and new permissions to tuples and performs the zanzana write after K8s update succeeds
// It handles both Role and CoreRole types
func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context, obj, oldObj runtime.Object, options *metav1.UpdateOptions) (registry.FinishFunc, error) {
if b.zClient == nil {
func (h *RoleHooks) BeginRoleUpdate(ctx context.Context, obj, oldObj runtime.Object, options *metav1.UpdateOptions) (registry.FinishFunc, error) {
if h.zClient == nil {
return nil, nil
}
var oldRole, newRole *iamv0.CoreRole
@@ -316,12 +327,12 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
// Grab a ticket to write to Zanzana
wait := time.Now()
b.zTickets <- true
h.zTickets <- true
hooksWaitHistogram.WithLabelValues(roleType, "update").Observe(time.Since(wait).Seconds()) // Record wait time
go func(old *iamv0.CoreRole, new *iamv0.CoreRole) {
defer func() {
<-b.zTickets
<-h.zTickets
}()
roleUID, namespace := old.Name, old.Namespace
oldPermissions, newPermissions := old.Spec.Permissions, new.Spec.Permissions
@@ -332,7 +343,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
var err error
oldTuples, err = convertRolePermissionsToTuples(roleUID, oldPermissions)
if err != nil {
b.logger.Error("failed to convert old role permissions to tuples",
h.logger.Error("failed to convert old role permissions to tuples",
"namespace", namespace,
"roleUID", roleUID,
"roleType", roleType,
@@ -344,7 +355,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
// Convert new permissions to tuples for writing
newTuples, err := convertRolePermissionsToTuples(roleUID, newPermissions)
if err != nil {
b.logger.Error("failed to convert new role permissions to tuples",
h.logger.Error("failed to convert new role permissions to tuples",
"namespace", namespace,
"roleUID", roleUID,
"roleType", roleType,
@@ -353,7 +364,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
return
}
b.logger.Debug("updating role permissions in zanzana",
h.logger.Debug("updating role permissions in zanzana",
"namespace", namespace,
"roleUID", roleUID,
"roleType", roleType,
@@ -375,7 +386,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
req.Deletes = &v1.WriteRequestDeletes{
TupleKeys: deleteTuples,
}
b.logger.Debug("deleting existing role permissions from zanzana",
h.logger.Debug("deleting existing role permissions from zanzana",
"namespace", namespace,
"roleUID", roleUID,
"roleType", roleType,
@@ -388,7 +399,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
req.Writes = &v1.WriteRequestWrites{
TupleKeys: newTuples,
}
b.logger.Debug("writing new role permissions to zanzana",
h.logger.Debug("writing new role permissions to zanzana",
"namespace", namespace,
"roleUID", roleUID,
"roleType", roleType,
@@ -398,9 +409,9 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
// Only make the request if there are deletes or writes
if req.Deletes != nil || req.Writes != nil {
err = b.zClient.Write(ctx, req)
err = h.zClient.Write(ctx, req)
if err != nil {
b.logger.Error("failed to update role permissions in zanzana",
h.logger.Error("failed to update role permissions in zanzana",
"err", err,
"namespace", namespace,
"roleUID", roleUID,
+46 -46
View File
@@ -53,7 +53,7 @@ func requireDeleteTuplesMatch(t *testing.T, actual []*v1.TupleKeyWithoutConditio
func TestAfterCoreRoleCreate(t *testing.T) {
var wg sync.WaitGroup
b := &IdentityAccessManagementAPIBuilder{
h := &RoleHooks{
logger: log.NewNopLogger(),
zTickets: make(chan bool, 1),
}
@@ -90,8 +90,8 @@ func TestAfterCoreRoleCreate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testCoreRoleEntries}
b.AfterRoleCreate(&coreRole, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testCoreRoleEntries}
h.AfterRoleCreate(&coreRole, nil)
wg.Wait()
})
@@ -129,8 +129,8 @@ func TestAfterCoreRoleCreate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testDashboardRoleEntries}
b.AfterRoleCreate(&coreRole, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testDashboardRoleEntries}
h.AfterRoleCreate(&coreRole, nil)
wg.Wait()
})
@@ -163,8 +163,8 @@ func TestAfterCoreRoleCreate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testWildcardEntries}
b.AfterRoleCreate(&coreRole, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testWildcardEntries}
h.AfterRoleCreate(&coreRole, nil)
wg.Wait()
})
@@ -198,15 +198,15 @@ func TestAfterCoreRoleCreate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testMixedEntries}
b.AfterRoleCreate(&coreRole, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testMixedEntries}
h.AfterRoleCreate(&coreRole, nil)
wg.Wait()
})
}
func TestAfterRoleCreate(t *testing.T) {
var wg sync.WaitGroup
b := &IdentityAccessManagementAPIBuilder{
h := &RoleHooks{
logger: log.NewNopLogger(),
zTickets: make(chan bool, 1),
}
@@ -243,8 +243,8 @@ func TestAfterRoleCreate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testRoleEntries}
b.AfterRoleCreate(&role, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testRoleEntries}
h.AfterRoleCreate(&role, nil)
wg.Wait()
})
@@ -281,8 +281,8 @@ func TestAfterRoleCreate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testDashRoleEntries}
b.AfterRoleCreate(&role, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testDashRoleEntries}
h.AfterRoleCreate(&role, nil)
wg.Wait()
})
@@ -317,15 +317,15 @@ func TestAfterRoleCreate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testMergedEntries}
b.AfterRoleCreate(&role, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testMergedEntries}
h.AfterRoleCreate(&role, nil)
wg.Wait()
})
}
func TestBeginCoreRoleUpdate(t *testing.T) {
var wg sync.WaitGroup
b := &IdentityAccessManagementAPIBuilder{
h := &RoleHooks{
logger: log.NewNopLogger(),
zTickets: make(chan bool, 1),
}
@@ -387,10 +387,10 @@ func TestBeginCoreRoleUpdate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testUpdate}
h.zClient = &FakeZanzanaClient{writeCallback: testUpdate}
// Call BeginUpdate which does all the work
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
require.NoError(t, err)
require.NotNil(t, finishFunc)
@@ -445,10 +445,10 @@ func TestBeginCoreRoleUpdate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testExpand}
h.zClient = &FakeZanzanaClient{writeCallback: testExpand}
// Call BeginUpdate which does all the work
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
require.NoError(t, err)
require.NotNil(t, finishFunc)
@@ -499,10 +499,10 @@ func TestBeginCoreRoleUpdate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testClear}
h.zClient = &FakeZanzanaClient{writeCallback: testClear}
// Call BeginUpdate which does all the work
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
require.NoError(t, err)
require.NotNil(t, finishFunc)
@@ -514,7 +514,7 @@ func TestBeginCoreRoleUpdate(t *testing.T) {
func TestBeginRoleUpdate(t *testing.T) {
var wg sync.WaitGroup
b := &IdentityAccessManagementAPIBuilder{
h := &RoleHooks{
logger: log.NewNopLogger(),
zTickets: make(chan bool, 1),
}
@@ -577,10 +577,10 @@ func TestBeginRoleUpdate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testUpdate}
h.zClient = &FakeZanzanaClient{writeCallback: testUpdate}
// Call BeginUpdate which does all the work
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
require.NoError(t, err)
require.NotNil(t, finishFunc)
@@ -640,10 +640,10 @@ func TestBeginRoleUpdate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testSwap}
h.zClient = &FakeZanzanaClient{writeCallback: testSwap}
// Call BeginUpdate which does all the work
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
require.NoError(t, err)
require.NotNil(t, finishFunc)
@@ -695,10 +695,10 @@ func TestBeginRoleUpdate(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testAddToEmpty}
h.zClient = &FakeZanzanaClient{writeCallback: testAddToEmpty}
// Call BeginUpdate which does all the work
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
require.NoError(t, err)
require.NotNil(t, finishFunc)
@@ -710,7 +710,7 @@ func TestBeginRoleUpdate(t *testing.T) {
func TestAfterCoreRoleDelete(t *testing.T) {
var wg sync.WaitGroup
b := &IdentityAccessManagementAPIBuilder{
h := &RoleHooks{
logger: log.NewNopLogger(),
zTickets: make(chan bool, 1),
}
@@ -747,8 +747,8 @@ func TestAfterCoreRoleDelete(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testCoreRoleDeletes}
b.AfterRoleDelete(&coreRole, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testCoreRoleDeletes}
h.AfterRoleDelete(&coreRole, nil)
wg.Wait()
})
@@ -785,8 +785,8 @@ func TestAfterCoreRoleDelete(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testDashboardRoleDeletes}
b.AfterRoleDelete(&coreRole, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testDashboardRoleDeletes}
h.AfterRoleDelete(&coreRole, nil)
wg.Wait()
})
@@ -818,8 +818,8 @@ func TestAfterCoreRoleDelete(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testWildcardDeletes}
b.AfterRoleDelete(&coreRole, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testWildcardDeletes}
h.AfterRoleDelete(&coreRole, nil)
wg.Wait()
})
@@ -853,15 +853,15 @@ func TestAfterCoreRoleDelete(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testMixedDeletes}
b.AfterRoleDelete(&coreRole, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testMixedDeletes}
h.AfterRoleDelete(&coreRole, nil)
wg.Wait()
})
}
func TestAfterRoleDelete(t *testing.T) {
var wg sync.WaitGroup
b := &IdentityAccessManagementAPIBuilder{
h := &RoleHooks{
logger: log.NewNopLogger(),
zTickets: make(chan bool, 1),
}
@@ -898,8 +898,8 @@ func TestAfterRoleDelete(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testRoleDeletes}
b.AfterRoleDelete(&role, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testRoleDeletes}
h.AfterRoleDelete(&role, nil)
wg.Wait()
})
@@ -936,8 +936,8 @@ func TestAfterRoleDelete(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testDashRoleDeletes}
b.AfterRoleDelete(&role, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testDashRoleDeletes}
h.AfterRoleDelete(&role, nil)
wg.Wait()
})
@@ -982,8 +982,8 @@ func TestAfterRoleDelete(t *testing.T) {
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testMultiDeletes}
b.AfterRoleDelete(&role, nil)
h.zClient = &FakeZanzanaClient{writeCallback: testMultiDeletes}
h.AfterRoleDelete(&role, nil)
wg.Wait()
})
}
+1 -1
View File
@@ -28,7 +28,7 @@ import (
var WireSetExts = wire.NewSet(
noopstorage.ProvideStorageBackend,
wire.Bind(new(iam.CoreRoleStorageBackend), new(*noopstorage.StorageBackendImpl)),
wire.Bind(new(iam.RoleStorageBackend), new(*noopstorage.StorageBackendImpl)),
iam.ProvideNoopRoleApiInstaller,
wire.Bind(new(iam.RoleBindingStorageBackend), new(*noopstorage.StorageBackendImpl)),
wire.Bind(new(iam.ExternalGroupMappingStorageBackend), new(*noopstorage.StorageBackendImpl)),
@@ -20,6 +20,7 @@ import (
"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications/timeinterval"
"github.com/grafana/grafana/pkg/services/apiserver/appinstaller"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ngalert"
ac "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
@@ -97,7 +98,12 @@ func (a AlertingNotificationsAppInstaller) GetLegacyStorage(gvr schema.GroupVers
} else if gvr == timeinterval.ResourceInfo.GroupVersionResource() {
return timeinterval.NewStorage(api.MuteTimings, namespacer)
} else if gvr == templategroup.ResourceInfo.GroupVersionResource() {
return templategroup.NewStorage(api.Templates, namespacer)
srv := api.Templates
//nolint:staticcheck // not yet migrated to OpenFeature
if a.ng.FeatureToggles.IsEnabledGlobally(featuremgmt.FlagAlertingImportAlertmanagerAPI) {
srv = srv.WithIncludeImported()
}
return templategroup.NewStorage(srv, namespacer)
} else if gvr == routingtree.ResourceInfo.GroupVersionResource() {
return routingtree.NewStorage(api.Policies, namespacer)
}
@@ -1,6 +1,7 @@
package templategroup
import (
"github.com/grafana/alerting/definition"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/types"
@@ -36,6 +37,7 @@ func convertToK8sResource(orgID int64, template definitions.NotificationTemplate
Spec: model.TemplateGroupSpec{
Title: template.Name,
Content: template.Template,
Kind: model.TemplateGroupTemplateKind(template.Kind),
},
}
result.SetProvenanceStatus(string(template.Provenance))
@@ -50,5 +52,6 @@ func convertToDomainModel(template *model.TemplateGroup) definitions.Notificatio
Template: template.Spec.Content,
ResourceVersion: template.ResourceVersion,
Provenance: definitions.Provenance(ngmodels.ProvenanceNone),
Kind: definition.TemplateKind(template.Spec.Kind),
}
}
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/grafana/alerting/definition"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -211,6 +212,7 @@ func (s *legacyStorage) defaultTemplate() (definitions.NotificationTemplate, err
UID: defaultTemplate.Name,
Provenance: definitions.Provenance("system"),
Template: defaultTemplate.Template,
Kind: definition.GrafanaTemplateKind,
}
return dto, nil
+4 -2
View File
@@ -882,8 +882,9 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
}
folderAPIBuilder := folders.RegisterAPIService(cfg, featureToggles, apiserverService, folderimplService, folderPermissionsService, accessControl, acimplService, accessClient, registerer, resourceClient, zanzanaClient)
storageBackendImpl := noopstorage.ProvideStorageBackend()
roleApiInstaller := iam.ProvideNoopRoleApiInstaller()
noopTeamGroupsREST := externalgroupmapping.ProvideNoopTeamGroupsREST()
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(cfg, featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, tracingService, storageBackendImpl, storageBackendImpl, noopTeamGroupsREST, dualwriteService, resourceClient, orgService, userService, teamService, eventualRestConfigProvider)
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(cfg, featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, roleApiInstaller, tracingService, storageBackendImpl, storageBackendImpl, noopTeamGroupsREST, dualwriteService, resourceClient, orgService, userService, teamService, eventualRestConfigProvider)
if err != nil {
return nil, err
}
@@ -1543,8 +1544,9 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
}
folderAPIBuilder := folders.RegisterAPIService(cfg, featureToggles, apiserverService, folderimplService, folderPermissionsService, accessControl, acimplService, accessClient, registerer, resourceClient, zanzanaClient)
storageBackendImpl := noopstorage.ProvideStorageBackend()
roleApiInstaller := iam.ProvideNoopRoleApiInstaller()
noopTeamGroupsREST := externalgroupmapping.ProvideNoopTeamGroupsREST()
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(cfg, featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, tracingService, storageBackendImpl, storageBackendImpl, noopTeamGroupsREST, dualwriteService, resourceClient, orgService, userService, teamService, eventualRestConfigProvider)
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(cfg, featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, roleApiInstaller, tracingService, storageBackendImpl, storageBackendImpl, noopTeamGroupsREST, dualwriteService, resourceClient, orgService, userService, teamService, eventualRestConfigProvider)
if err != nil {
return nil, err
}
+7
View File
@@ -1360,6 +1360,13 @@ var (
Owner: grafanaAlertingSquad,
HideFromDocs: true,
},
{
Name: "fetchRulesInCompactMode",
Description: "Add compact=true when fetching rules",
Stage: FeatureStageExperimental,
Owner: grafanaAlertingSquad,
HideFromDocs: true,
},
{
Name: "newLogsPanel",
Description: "Enables the new logs panel",
+1
View File
@@ -188,6 +188,7 @@ grafanaAdvisor,privatePreview,@grafana/plugins-platform-backend,false,false,fals
elasticsearchImprovedParsing,experimental,@grafana/partner-datasources,false,false,false
datasourceConnectionsTab,privatePreview,@grafana/plugins-platform-backend,false,false,true
fetchRulesUsingPost,experimental,@grafana/alerting-squad,false,false,false
fetchRulesInCompactMode,experimental,@grafana/alerting-squad,false,false,false
newLogsPanel,GA,@grafana/observability-logs,false,false,true
grafanaconThemes,GA,@grafana/grafana-frontend-platform,false,true,false
alertingJiraIntegration,experimental,@grafana/alerting-squad,false,false,true
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
188 elasticsearchImprovedParsing experimental @grafana/partner-datasources false false false
189 datasourceConnectionsTab privatePreview @grafana/plugins-platform-backend false false true
190 fetchRulesUsingPost experimental @grafana/alerting-squad false false false
191 fetchRulesInCompactMode experimental @grafana/alerting-squad false false false
192 newLogsPanel GA @grafana/observability-logs false false true
193 grafanaconThemes GA @grafana/grafana-frontend-platform false true false
194 alertingJiraIntegration experimental @grafana/alerting-squad false false true
+4
View File
@@ -571,6 +571,10 @@ const (
// Use a POST request to list rules by passing down the namespaces user has access to
FlagFetchRulesUsingPost = "fetchRulesUsingPost"
// FlagFetchRulesInCompactMode
// Add compact=true when fetching rules
FlagFetchRulesInCompactMode = "fetchRulesInCompactMode"
// FlagGrafanaconThemes
// Enables the temporary themes for GrafanaCon
FlagGrafanaconThemes = "grafanaconThemes"
+13
View File
@@ -1457,6 +1457,19 @@
"expression": "false"
}
},
{
"metadata": {
"name": "fetchRulesInCompactMode",
"resourceVersion": "1766045288124",
"creationTimestamp": "2025-12-18T08:08:08Z"
},
"spec": {
"description": "Add compact=true when fetching rules",
"stage": "experimental",
"codeowner": "@grafana/alerting-squad",
"hideFromDocs": true
}
},
{
"metadata": {
"name": "fetchRulesUsingPost",
@@ -439,7 +439,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
ID: fooFolder.ID, // nolint:staticcheck
UID: fooFolder.UID,
},
}, nil).Once()
}, nil).Twice() // Called twice due to total count call
id := int64(123)
emptyString := ""
query := &folder.GetFolderQuery{
@@ -455,7 +455,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
})
t.Run("When get folder by non existing ID should return not found error", func(t *testing.T) {
dashboardStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Once()
dashboardStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Twice() // Called twice due to total count call
id := int64(111111)
query := &folder.GetFolderQuery{
ID: &id,
@@ -475,7 +475,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
ID: fooFolder.ID, // nolint:staticcheck
UID: fooFolder.UID,
},
}, nil).Once()
}, nil).Twice() // Called twice due to total count call
title := "foo"
query := &folder.GetFolderQuery{
Title: &title,
@@ -489,7 +489,7 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
})
t.Run("When get folder by non existing Title should return not found error", func(t *testing.T) {
dashboardStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Once()
dashboardStore.On("FindDashboards", mock.Anything, mock.Anything).Return([]dashboards.DashboardSearchProjection{}, nil).Twice() // Called twice due to total count call
title := "does not exists"
query := &folder.GetFolderQuery{
Title: &title,
@@ -5,7 +5,8 @@ import (
"regexp"
"strings"
"github.com/grafana/alerting/templates"
"github.com/grafana/alerting/definition"
"github.com/grafana/alerting/notify"
"go.yaml.in/yaml/v3"
)
@@ -31,11 +32,18 @@ func (t *NotificationTemplate) Validate() error {
content = fmt.Sprintf("{{ define \"%s\" }}\n%s\n{{ end }}", t.Name, content)
}
t.Template = content
def := templates.TemplateDefinition{
Name: t.Name,
Template: t.Template,
Kind: templates.GrafanaKind,
if t.Kind == "" {
t.Kind = definition.GrafanaTemplateKind
}
postable := definition.PostableApiTemplate{
Name: t.Name,
Content: t.Template,
Kind: t.Kind,
}
if err := postable.Validate(); err != nil {
return err
}
def := notify.PostableAPITemplateToTemplateDefinition(postable)
return def.Validate()
}
@@ -462,6 +462,16 @@ func TestValidateNotificationTemplates(t *testing.T) {
},
expContent: `{{ define "Alert Instance Template" }}\nFiring: {{ .Labels.alertname }}\nSilence: {{ .SilenceURL }}\n{{ end }}[what is this?]`,
},
{
name: "unknown template kind",
template: NotificationTemplate{
Name: "Alert Instance Template",
Template: `{{ define "Same name as definition" }}\nFiring: {{ .Labels.alertname }}\nSilence: {{ .SilenceURL }}\n{{ end }}`,
Provenance: "test",
Kind: "unknown",
},
expError: errors.New("unknown template kind: unknown"),
},
}
for _, tt := range tc {
@@ -1,5 +1,7 @@
package definitions
import "github.com/grafana/alerting/definition"
// swagger:route GET /v1/provisioning/templates provisioning stable RouteGetTemplates
//
// Get all notification template groups.
@@ -55,11 +57,12 @@ type RouteDeleteTemplateParam struct {
// swagger:model
type NotificationTemplate struct {
UID string `json:"-" yaml:"-"`
Name string `json:"name"`
Template string `json:"template"`
Provenance Provenance `json:"provenance,omitempty"`
ResourceVersion string `json:"version,omitempty"`
UID string `json:"-" yaml:"-"`
Name string `json:"name"`
Template string `json:"template"`
Provenance Provenance `json:"provenance,omitempty"`
ResourceVersion string `json:"version,omitempty"`
Kind definition.TemplateKind `json:"-" yaml:"-"`
}
// swagger:model
@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/grafana/grafana/pkg/apimachinery/errutil"
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models"
)
@@ -25,6 +26,10 @@ var (
ErrTemplateNotFound = errutil.NotFound("alerting.notifications.templates.notFound")
ErrTemplateInvalid = errutil.BadRequest("alerting.notifications.templates.invalidFormat").MustTemplate("Invalid format of the submitted template", errutil.WithPublic("Template is in invalid format. Correct the payload and try again."))
ErrTemplateExists = errutil.BadRequest("alerting.notifications.templates.nameExists", errutil.WithPublicMessage("Template file with this name already exists. Use a different name or update existing one."))
ErrTemplateOrigin = errutil.BadRequest("alerting.notifications.templates.originInvalid").MustTemplate(
"Template '{{ .Public.Name }}' cannot be {{ .Public.Action }}d because it belongs to an imported configuration.",
errutil.WithPublic("Template '{{ .Public.Name }}' cannot be {{ .Public.Action }}d because it belongs to an imported configuration. Finish the import of the configuration first."),
)
ErrContactPointReferenced = errutil.Conflict("alerting.notifications.contact-points.referenced", errutil.WithPublicMessage("Contact point is currently referenced by a notification policy."))
ErrContactPointUsedInRule = errutil.Conflict("alerting.notifications.contact-points.used-by-rule", errutil.WithPublicMessage("Contact point is currently used in the notification settings of one or many alert rules."))
@@ -129,3 +134,7 @@ func MakeErrContactPointUidExists(uid, name string) error {
},
})
}
func makeErrTemplateOrigin(t definitions.NotificationTemplate, action string) error {
return ErrTemplateOrigin.Build(errutil.TemplateData{Public: map[string]interface{}{"Action": action, "Name": t.Name}})
}
+153 -102
View File
@@ -10,6 +10,8 @@ import (
"sort"
"unsafe"
"github.com/grafana/alerting/definition"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models"
@@ -23,6 +25,7 @@ type TemplateService struct {
xact TransactionManager
log log.Logger
validator validation.ProvenanceStatusTransitionValidator
includeImported bool
}
func NewTemplateService(config alertmanagerConfigStore, prov ProvisioningStore, xact TransactionManager, log log.Logger) *TemplateService {
@@ -32,6 +35,18 @@ func NewTemplateService(config alertmanagerConfigStore, prov ProvisioningStore,
xact: xact,
validator: validation.ValidateProvenanceRelaxed,
log: log,
includeImported: false,
}
}
func (t *TemplateService) WithIncludeImported() *TemplateService {
return &TemplateService{
configStore: t.configStore,
provenanceStore: t.provenanceStore,
xact: t.xact,
validator: t.validator,
log: t.log,
includeImported: true,
}
}
@@ -41,35 +56,38 @@ func (t *TemplateService) GetTemplates(ctx context.Context, orgID int64) ([]defi
return nil, err
}
if len(revision.Config.TemplateFiles) == 0 {
return nil, nil
}
var templates []definitions.NotificationTemplate
provenances, err := t.provenanceStore.GetProvenances(ctx, orgID, (&definitions.NotificationTemplate{}).ResourceType())
if err != nil {
return nil, err
}
templates := make([]definitions.NotificationTemplate, 0, len(revision.Config.TemplateFiles))
names := slices.Collect(maps.Keys(revision.Config.TemplateFiles))
sort.Strings(names)
for _, name := range names {
content := revision.Config.TemplateFiles[name]
tmpl := definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(name),
Name: name,
Template: content,
ResourceVersion: calculateTemplateFingerprint(content),
if len(revision.Config.TemplateFiles) > 0 {
provenances, err := t.provenanceStore.GetProvenances(ctx, orgID, (&definitions.NotificationTemplate{}).ResourceType())
if err != nil {
return nil, err
}
provenance, ok := provenances[tmpl.ResourceID()]
if !ok {
provenance = models.ProvenanceNone
templates = make([]definitions.NotificationTemplate, 0, len(revision.Config.TemplateFiles))
names := slices.Collect(maps.Keys(revision.Config.TemplateFiles))
sort.Strings(names)
for _, name := range names {
content := revision.Config.TemplateFiles[name]
provenance, ok := provenances[(&definitions.NotificationTemplate{Name: name}).ResourceID()]
if !ok {
provenance = models.ProvenanceNone
}
templates = append(templates, newNotificationTemplate(name, content, provenance, definition.GrafanaTemplateKind))
}
tmpl.Provenance = definitions.Provenance(provenance)
templates = append(templates, tmpl)
}
return templates, nil
var importedTemplates []definitions.NotificationTemplate
if t.includeImported && len(revision.Config.ExtraConfigs) > 0 && len(revision.Config.ExtraConfigs[0].TemplateFiles) > 0 {
imported := revision.Config.ExtraConfigs[0].TemplateFiles
importedTemplates = make([]definitions.NotificationTemplate, 0, len(imported))
names := slices.Collect(maps.Keys(imported))
sort.Strings(names)
for _, name := range names {
content := imported[name]
templates = append(templates, newNotificationTemplate(name, content, models.ProvenanceConvertedPrometheus, definition.MimirTemplateKind))
}
}
return append(templates, importedTemplates...), nil
}
func (t *TemplateService) GetTemplate(ctx context.Context, orgID int64, nameOrUid string) (definitions.NotificationTemplate, error) {
@@ -77,29 +95,21 @@ func (t *TemplateService) GetTemplate(ctx context.Context, orgID int64, nameOrUi
if err != nil {
return definitions.NotificationTemplate{}, err
}
existingName := nameOrUid
existingContent, ok := revision.Config.TemplateFiles[nameOrUid]
if !ok {
existingName, existingContent, ok = getTemplateByUid(revision.Config.TemplateFiles, nameOrUid)
}
if !ok {
return definitions.NotificationTemplate{}, ErrTemplateNotFound.Errorf("")
}
tmpl := definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(existingName),
Name: existingName,
Template: existingContent,
ResourceVersion: calculateTemplateFingerprint(existingContent),
}
provenance, err := t.provenanceStore.GetProvenance(ctx, &tmpl, orgID)
result, found, err := t.getTemplateByName(ctx, revision, orgID, nameOrUid)
if err != nil {
return definitions.NotificationTemplate{}, err
}
tmpl.Provenance = definitions.Provenance(provenance)
return tmpl, nil
if found {
return result, nil
}
result, found, err = t.getTemplateByUID(ctx, revision, orgID, nameOrUid)
if err != nil {
return definitions.NotificationTemplate{}, err
}
if found {
return result, nil
}
return definitions.NotificationTemplate{}, ErrTemplateNotFound.Errorf("")
}
func (t *TemplateService) UpsertTemplate(ctx context.Context, orgID int64, tmpl definitions.NotificationTemplate) (definitions.NotificationTemplate, error) {
@@ -135,6 +145,10 @@ func (t *TemplateService) CreateTemplate(ctx context.Context, orgID int64, tmpl
if err != nil {
return definitions.NotificationTemplate{}, MakeErrTemplateInvalid(err)
}
if tmpl.Kind == definition.MimirTemplateKind {
return definitions.NotificationTemplate{}, MakeErrTemplateInvalid(errors.New("templates of kind 'Mimir' cannot be created"))
}
revision, err := t.configStore.Get(ctx, orgID)
if err != nil {
return definitions.NotificationTemplate{}, err
@@ -143,6 +157,10 @@ func (t *TemplateService) CreateTemplate(ctx context.Context, orgID int64, tmpl
}
func (t *TemplateService) createTemplate(ctx context.Context, revision *legacy_storage.ConfigRevision, orgID int64, tmpl definitions.NotificationTemplate) (definitions.NotificationTemplate, error) {
if tmpl.Kind == definition.MimirTemplateKind {
return definitions.NotificationTemplate{}, MakeErrTemplateInvalid(errors.New("templates of kind 'Mimir' cannot be created"))
}
if revision.Config.TemplateFiles == nil {
revision.Config.TemplateFiles = map[string]string{}
}
@@ -164,13 +182,7 @@ func (t *TemplateService) createTemplate(ctx context.Context, revision *legacy_s
return definitions.NotificationTemplate{}, err
}
return definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(tmpl.Name),
Name: tmpl.Name,
Template: tmpl.Template,
Provenance: tmpl.Provenance,
ResourceVersion: calculateTemplateFingerprint(tmpl.Template),
}, nil
return newNotificationTemplate(tmpl.Name, tmpl.Template, models.Provenance(tmpl.Provenance), tmpl.Kind), nil
}
func (t *TemplateService) UpdateTemplate(ctx context.Context, orgID int64, tmpl definitions.NotificationTemplate) (definitions.NotificationTemplate, error) {
@@ -192,37 +204,39 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
}
var found bool
var existingName, existingContent string
var err error
var existing definitions.NotificationTemplate
// if UID is specified, look by UID.
if tmpl.UID != "" {
existingName, existingContent, found = getTemplateByUid(revision.Config.TemplateFiles, tmpl.UID)
// do not fall back to name because we address by UID, and resource can be deleted\renamed
existing, found, err = t.getTemplateByUID(ctx, revision, orgID, tmpl.UID)
} else {
existingName = tmpl.Name
existingContent, found = revision.Config.TemplateFiles[existingName]
existing, found, err = t.getTemplateByName(ctx, revision, orgID, tmpl.Name)
}
if err != nil {
return definitions.NotificationTemplate{}, err
}
if !found {
return definitions.NotificationTemplate{}, ErrTemplateNotFound.Errorf("")
}
if existingName != tmpl.Name { // if template is renamed, check if this name is already taken
if existing.Name != tmpl.Name { // if template is renamed, check if this name is already taken
_, ok := revision.Config.TemplateFiles[tmpl.Name]
if ok {
// return error if template is being renamed to one that already exists
return definitions.NotificationTemplate{}, ErrTemplateExists.Errorf("")
}
}
// check that provenance is not changed in an invalid way
storedProvenance, err := t.provenanceStore.GetProvenance(ctx, &tmpl, orgID)
if err != nil {
return definitions.NotificationTemplate{}, err
if existing.Kind != tmpl.Kind {
return definitions.NotificationTemplate{}, MakeErrTemplateInvalid(errors.New("cannot change template kind"))
}
if err := t.validator(storedProvenance, models.Provenance(tmpl.Provenance)); err != nil {
if existing.Provenance == definitions.Provenance(models.ProvenanceConvertedPrometheus) {
return definitions.NotificationTemplate{}, makeErrTemplateOrigin(existing, "update")
}
if err := t.validator(models.Provenance(existing.Provenance), models.Provenance(tmpl.Provenance)); err != nil {
return definitions.NotificationTemplate{}, err
}
err = t.checkOptimisticConcurrency(tmpl.Name, existingContent, models.Provenance(tmpl.Provenance), tmpl.ResourceVersion, "update")
err = t.checkOptimisticConcurrency(existing.Name, existing.Template, models.Provenance(tmpl.Provenance), tmpl.ResourceVersion, "update")
if err != nil {
return definitions.NotificationTemplate{}, err
}
@@ -230,9 +244,9 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
revision.Config.TemplateFiles[tmpl.Name] = tmpl.Template
err = t.xact.InTransaction(ctx, func(ctx context.Context) error {
if existingName != tmpl.Name { // if template by was found by UID and it's name is different, then this is the rename operation. Delete old resources.
delete(revision.Config.TemplateFiles, existingName)
err := t.provenanceStore.DeleteProvenance(ctx, &definitions.NotificationTemplate{Name: existingName}, orgID)
if existing.Name != tmpl.Name { // if template by was found by UID and it's name is different, then this is the rename operation. Delete old resources.
delete(revision.Config.TemplateFiles, existing.Name)
err := t.provenanceStore.DeleteProvenance(ctx, &existing, orgID)
if err != nil {
return err
}
@@ -247,13 +261,8 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
return definitions.NotificationTemplate{}, err
}
return definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(tmpl.Name), // if name was changed, this UID will not match the incoming one
Name: tmpl.Name,
Template: tmpl.Template,
Provenance: tmpl.Provenance,
ResourceVersion: calculateTemplateFingerprint(tmpl.Template),
}, nil
// if name was changed, this UID needs to be recalculated
return newNotificationTemplate(tmpl.Name, tmpl.Template, models.Provenance(tmpl.Provenance), tmpl.Kind), nil
}
func (t *TemplateService) DeleteTemplate(ctx context.Context, orgID int64, nameOrUid string, provenance definitions.Provenance, version string) error {
@@ -261,44 +270,39 @@ func (t *TemplateService) DeleteTemplate(ctx context.Context, orgID int64, nameO
if err != nil {
return err
}
if revision.Config.TemplateFiles == nil {
existing, found, err := t.getTemplateByName(ctx, revision, orgID, nameOrUid)
if err != nil {
return err
}
if !found {
existing, found, err = t.getTemplateByUID(ctx, revision, orgID, nameOrUid)
}
if err != nil {
return err
}
if !found {
return nil
}
existingName := nameOrUid
existing, ok := revision.Config.TemplateFiles[nameOrUid]
if !ok {
existingName, existing, ok = getTemplateByUid(revision.Config.TemplateFiles, nameOrUid)
}
if !ok {
return nil
if existing.Provenance == definitions.Provenance(models.ProvenanceConvertedPrometheus) {
return makeErrTemplateOrigin(existing, "delete")
}
err = t.checkOptimisticConcurrency(existingName, existing, models.Provenance(provenance), version, "delete")
err = t.checkOptimisticConcurrency(existing.Name, existing.Template, models.Provenance(provenance), version, "delete")
if err != nil {
return err
}
// check that provenance is not changed in an invalid way
storedProvenance, err := t.provenanceStore.GetProvenance(ctx, &definitions.NotificationTemplate{Name: existingName}, orgID)
if err != nil {
return err
}
if err = t.validator(storedProvenance, models.Provenance(provenance)); err != nil {
if err = t.validator(models.Provenance(existing.Provenance), models.Provenance(provenance)); err != nil {
return err
}
delete(revision.Config.TemplateFiles, existingName)
delete(revision.Config.TemplateFiles, existing.Name)
return t.xact.InTransaction(ctx, func(ctx context.Context) error {
if err := t.configStore.Save(ctx, revision, orgID); err != nil {
return err
}
tgt := definitions.NotificationTemplate{
Name: existingName,
}
return t.provenanceStore.DeleteProvenance(ctx, &tgt, orgID)
return t.provenanceStore.DeleteProvenance(ctx, &existing, orgID)
})
}
@@ -323,11 +327,58 @@ func calculateTemplateFingerprint(t string) string {
return fmt.Sprintf("%016x", sum.Sum64())
}
func getTemplateByUid(templates map[string]string, uid string) (string, string, bool) {
for n, tmpl := range templates {
if legacy_storage.NameToUid(n) == uid {
return n, tmpl, true
}
func newNotificationTemplate(name, content string, provenance models.Provenance, kind definition.TemplateKind) definitions.NotificationTemplate {
tmpl := definitions.NotificationTemplate{
UID: templateUID(kind, name),
Name: name,
Template: content,
Provenance: definitions.Provenance(provenance),
Kind: kind,
}
return "", "", false
tmpl.ResourceVersion = calculateTemplateFingerprint(content)
return tmpl
}
func (t *TemplateService) getTemplateByName(ctx context.Context, revision *legacy_storage.ConfigRevision, orgID int64, name string) (definitions.NotificationTemplate, bool, error) {
existingContent, ok := revision.Config.TemplateFiles[name]
if !ok {
return definitions.NotificationTemplate{}, false, nil
}
provenance, err := t.provenanceStore.GetProvenance(ctx, &definitions.NotificationTemplate{Name: name}, orgID)
if err != nil {
return definitions.NotificationTemplate{}, false, err
}
return newNotificationTemplate(name, existingContent, provenance, definition.GrafanaTemplateKind), true, nil
}
func (t *TemplateService) getTemplateByUID(ctx context.Context, revision *legacy_storage.ConfigRevision, orgID int64, uid string) (definitions.NotificationTemplate, bool, error) {
find := func(templates map[string]string, uid string, kind definition.TemplateKind) (string, string, bool) {
for n, tmpl := range templates {
if templateUID(kind, n) == uid {
return n, tmpl, true
}
}
return "", "", false
}
var provenance models.Provenance
name, content, ok := find(revision.Config.TemplateFiles, uid, definition.GrafanaTemplateKind)
if !ok {
if t.includeImported && len(revision.Config.ExtraConfigs) > 0 {
name, content, ok = find(revision.Config.ExtraConfigs[0].TemplateFiles, uid, definition.MimirTemplateKind)
if ok {
return newNotificationTemplate(name, content, models.ProvenanceConvertedPrometheus, definition.MimirTemplateKind), true, nil
}
}
return definitions.NotificationTemplate{}, false, nil
}
var err error
provenance, err = t.provenanceStore.GetProvenance(ctx, &definitions.NotificationTemplate{Name: name}, orgID)
if err != nil {
return definitions.NotificationTemplate{}, false, err
}
return newNotificationTemplate(name, content, provenance, definition.GrafanaTemplateKind), true, nil
}
func templateUID(kind definition.TemplateKind, name string) string {
return legacy_storage.NameToUid(fmt.Sprintf("%s|%s", string(kind), name))
}
@@ -6,6 +6,7 @@ import (
"fmt"
"testing"
"github.com/grafana/alerting/definition"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
@@ -27,6 +28,15 @@ func TestGetTemplates(t *testing.T) {
"template2": "test2",
"template3": "test3",
},
ExtraConfigs: []definitions.ExtraConfiguration{
{
Identifier: "1234",
TemplateFiles: map[string]string{
"template1": "imported-test1",
"template4": "imported-test4",
},
},
},
},
}
@@ -45,27 +55,24 @@ func TestGetTemplates(t *testing.T) {
require.NoError(t, err)
expected := []definitions.NotificationTemplate{
{
UID: legacy_storage.NameToUid("template1"),
Name: "template1",
Template: "test1",
Provenance: definitions.Provenance(models.ProvenanceAPI),
ResourceVersion: calculateTemplateFingerprint("test1"),
},
{
UID: legacy_storage.NameToUid("template2"),
Name: "template2",
Template: "test2",
Provenance: definitions.Provenance(models.ProvenanceFile),
ResourceVersion: calculateTemplateFingerprint("test2"),
},
{
UID: legacy_storage.NameToUid("template3"),
Name: "template3",
Template: "test3",
Provenance: definitions.Provenance(models.ProvenanceNone),
ResourceVersion: calculateTemplateFingerprint("test3"),
},
newNotificationTemplate(
"template1",
"test1",
models.ProvenanceAPI,
definition.GrafanaTemplateKind,
),
newNotificationTemplate(
"template2",
"test2",
models.ProvenanceFile,
definition.GrafanaTemplateKind,
),
newNotificationTemplate(
"template3",
"test3",
models.ProvenanceNone,
definition.GrafanaTemplateKind,
),
}
require.EqualValues(t, expected, result)
@@ -89,6 +96,60 @@ func TestGetTemplates(t *testing.T) {
prov.AssertExpectations(t)
})
t.Run("returns imported templates if enabled", func(t *testing.T) {
sut, store, prov := createTemplateServiceSut()
sut = sut.WithIncludeImported()
store.GetFn = func(ctx context.Context, org int64) (*legacy_storage.ConfigRevision, error) {
assert.Equal(t, orgID, org)
return revision, nil
}
prov.EXPECT().GetProvenances(mock.Anything, mock.Anything, mock.Anything).Return(map[string]models.Provenance{
"template1": models.ProvenanceAPI,
"template2": models.ProvenanceFile,
}, nil)
result, err := sut.GetTemplates(context.Background(), orgID)
require.NoError(t, err)
expected := []definitions.NotificationTemplate{
newNotificationTemplate(
"template1",
"test1",
models.ProvenanceAPI,
definition.GrafanaTemplateKind,
),
newNotificationTemplate(
"template2",
"test2",
models.ProvenanceFile,
definition.GrafanaTemplateKind,
),
newNotificationTemplate(
"template3",
"test3",
models.ProvenanceNone,
definition.GrafanaTemplateKind,
),
newNotificationTemplate(
"template1",
"imported-test1",
models.ProvenanceConvertedPrometheus,
definition.MimirTemplateKind,
),
newNotificationTemplate(
"template4",
"imported-test4",
models.ProvenanceConvertedPrometheus,
definition.MimirTemplateKind,
),
}
require.EqualValues(t, expected, result)
prov.AssertCalled(t, "GetProvenances", mock.Anything, orgID, (&definitions.NotificationTemplate{}).ResourceType())
prov.AssertExpectations(t)
})
t.Run("propagates errors", func(t *testing.T) {
t.Run("when unable to read config", func(t *testing.T) {
sut, store, prov := createTemplateServiceSut()
@@ -127,15 +188,25 @@ func TestGetTemplate(t *testing.T) {
orgID := int64(1)
templateName := "template1"
templateContent := "test1"
importedTemplateName := "template2"
importedTemplateContent := "imported"
revision := &legacy_storage.ConfigRevision{
Config: &definitions.PostableUserConfig{
TemplateFiles: map[string]string{
templateName: templateContent,
},
ExtraConfigs: []definitions.ExtraConfiguration{
{
Identifier: "1234",
TemplateFiles: map[string]string{
importedTemplateName: importedTemplateContent,
},
},
},
},
}
t.Run("return a template from config file by name", func(t *testing.T) {
t.Run("return a template from config by name", func(t *testing.T) {
sut, store, prov := createTemplateServiceSut()
store.GetFn = func(ctx context.Context, org int64) (*legacy_storage.ConfigRevision, error) {
assert.Equal(t, orgID, org)
@@ -146,13 +217,12 @@ func TestGetTemplate(t *testing.T) {
result, err := sut.GetTemplate(context.Background(), orgID, templateName)
require.NoError(t, err)
expected := definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(templateName),
Name: templateName,
Template: templateContent,
Provenance: definitions.Provenance(models.ProvenanceAPI),
ResourceVersion: calculateTemplateFingerprint(templateContent),
}
expected := newNotificationTemplate(
templateName,
templateContent,
models.ProvenanceAPI,
definition.GrafanaTemplateKind,
)
require.Equal(t, expected, result)
@@ -162,6 +232,62 @@ func TestGetTemplate(t *testing.T) {
prov.AssertExpectations(t)
})
t.Run("imported templates cannot be retrieved by name", func(t *testing.T) {
sut, store, _ := createTemplateServiceSut()
store.GetFn = func(ctx context.Context, org int64) (*legacy_storage.ConfigRevision, error) {
assert.Equal(t, orgID, org)
return revision, nil
}
_, err := sut.GetTemplate(context.Background(), orgID, importedTemplateName)
require.ErrorIs(t, err, ErrTemplateNotFound)
})
t.Run("return a template from config by UID", func(t *testing.T) {
sut, store, prov := createTemplateServiceSut()
store.GetFn = func(ctx context.Context, org int64) (*legacy_storage.ConfigRevision, error) {
assert.Equal(t, orgID, org)
return revision, nil
}
prov.EXPECT().GetProvenance(mock.Anything, mock.Anything, mock.Anything).Return(models.ProvenanceNone, nil)
result, err := sut.GetTemplate(context.Background(), orgID, templateUID(definition.GrafanaTemplateKind, templateName))
require.NoError(t, err)
expected := newNotificationTemplate(
templateName,
templateContent,
models.ProvenanceNone,
definition.GrafanaTemplateKind,
)
require.Equal(t, expected, result)
})
t.Run("return an imported template from config by UID", func(t *testing.T) {
sut, store, prov := createTemplateServiceSut()
store.GetFn = func(ctx context.Context, org int64) (*legacy_storage.ConfigRevision, error) {
assert.Equal(t, orgID, org)
return revision, nil
}
uid := templateUID(definition.MimirTemplateKind, importedTemplateName)
t.Run("should be not found without flag enabled", func(t *testing.T) {
_, err := sut.GetTemplate(context.Background(), orgID, uid)
require.ErrorIs(t, err, ErrTemplateNotFound)
})
result, err := sut.WithIncludeImported().GetTemplate(context.Background(), orgID, uid)
require.NoError(t, err)
expected := newNotificationTemplate(
importedTemplateName,
importedTemplateContent,
models.ProvenanceConvertedPrometheus,
definition.MimirTemplateKind,
)
require.Equal(t, expected, result)
prov.AssertExpectations(t)
})
t.Run("returns ErrTemplateNotFound when template does not exist", func(t *testing.T) {
sut, store, prov := createTemplateServiceSut()
store.GetFn = func(ctx context.Context, org int64) (*legacy_storage.ConfigRevision, error) {
@@ -242,18 +368,18 @@ func TestUpsertTemplate(t *testing.T) {
Template: "{{ define \"test\"}} test {{ end }}",
Provenance: definitions.Provenance(models.ProvenanceAPI),
ResourceVersion: "",
Kind: definition.GrafanaTemplateKind,
}
result, err := sut.UpsertTemplate(context.Background(), orgID, tmpl)
require.NoError(t, err)
require.Equal(t, definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(tmpl.Name),
Name: tmpl.Name,
Template: tmpl.Template,
Provenance: tmpl.Provenance,
ResourceVersion: calculateTemplateFingerprint(tmpl.Template),
}, result)
require.Equal(t, newNotificationTemplate(
tmpl.Name,
tmpl.Template,
models.Provenance(tmpl.Provenance),
tmpl.Kind,
), result)
require.Len(t, store.Calls, 2)
@@ -284,18 +410,18 @@ func TestUpsertTemplate(t *testing.T) {
Template: "{{ define \"test\"}} test {{ end }}",
Provenance: definitions.Provenance(models.ProvenanceAPI),
ResourceVersion: calculateTemplateFingerprint("test1"),
Kind: definition.GrafanaTemplateKind,
}
result, err := sut.UpsertTemplate(context.Background(), orgID, tmpl)
require.NoError(t, err)
assert.Equal(t, definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(tmpl.Name),
Name: tmpl.Name,
Template: tmpl.Template,
Provenance: tmpl.Provenance,
ResourceVersion: calculateTemplateFingerprint(tmpl.Template),
}, result)
assert.Equal(t, newNotificationTemplate(
tmpl.Name,
tmpl.Template,
models.Provenance(tmpl.Provenance),
tmpl.Kind,
), result)
require.Len(t, store.Calls, 2)
require.Equal(t, "Save", store.Calls[1].Method)
@@ -326,13 +452,12 @@ func TestUpsertTemplate(t *testing.T) {
result, err := sut.UpsertTemplate(context.Background(), orgID, tmpl)
require.NoError(t, err)
assert.Equal(t, definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(tmpl.Name),
Name: tmpl.Name,
Template: tmpl.Template,
Provenance: tmpl.Provenance,
ResourceVersion: calculateTemplateFingerprint(tmpl.Template),
}, result)
assert.Equal(t, newNotificationTemplate(
tmpl.Name,
tmpl.Template,
models.Provenance(tmpl.Provenance),
definition.GrafanaTemplateKind,
), result)
require.Equal(t, "Save", store.Calls[1].Method)
saved := store.Calls[1].Args[1].(*legacy_storage.ConfigRevision)
@@ -356,18 +481,18 @@ func TestUpsertTemplate(t *testing.T) {
Template: "content",
Provenance: definitions.Provenance(models.ProvenanceNone),
ResourceVersion: calculateTemplateFingerprint(currentTemplateContent),
Kind: definition.GrafanaTemplateKind,
}
result, _ := sut.UpsertTemplate(context.Background(), orgID, tmpl)
expectedContent := fmt.Sprintf("{{ define \"%s\" }}\n content\n{{ end }}", templateName)
require.Equal(t, definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(tmpl.Name),
Name: tmpl.Name,
Template: expectedContent,
Provenance: tmpl.Provenance,
ResourceVersion: calculateTemplateFingerprint(expectedContent),
}, result)
require.Equal(t, newNotificationTemplate(
tmpl.Name,
expectedContent,
models.Provenance(tmpl.Provenance),
tmpl.Kind,
), result)
})
t.Run("does not reject template with unknown field", func(t *testing.T) {
@@ -489,6 +614,21 @@ func TestUpsertTemplate(t *testing.T) {
require.ErrorIs(t, err, ErrTemplateNotFound)
})
t.Run("rejects new templates of mimir kind", func(t *testing.T) {
sut, store, _ := createTemplateServiceSut()
store.GetFn = func(ctx context.Context, org int64) (*legacy_storage.ConfigRevision, error) {
return revision(), nil
}
template := definitions.NotificationTemplate{
Name: "template2",
Template: "asdf-new",
Provenance: definitions.Provenance(models.ProvenanceNone),
Kind: definition.MimirTemplateKind,
}
_, err := sut.UpsertTemplate(context.Background(), orgID, template)
require.ErrorIs(t, err, ErrTemplateInvalid)
})
t.Run("propagates errors", func(t *testing.T) {
tmpl := definitions.NotificationTemplate{
Name: templateName,
@@ -562,6 +702,7 @@ func TestCreateTemplate(t *testing.T) {
Name: "new-template",
Template: "{{ define \"test\"}} test {{ end }}",
Provenance: definitions.Provenance(models.ProvenanceAPI),
Kind: definition.GrafanaTemplateKind,
}
revision := func() *legacy_storage.ConfigRevision {
@@ -588,13 +729,12 @@ func TestCreateTemplate(t *testing.T) {
result, err := sut.CreateTemplate(context.Background(), orgID, tmpl)
require.NoError(t, err)
require.Equal(t, definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(tmpl.Name),
Name: tmpl.Name,
Template: tmpl.Template,
Provenance: tmpl.Provenance,
ResourceVersion: calculateTemplateFingerprint(tmpl.Template),
}, result)
require.Equal(t, newNotificationTemplate(
tmpl.Name,
tmpl.Template,
models.Provenance(tmpl.Provenance),
tmpl.Kind,
), result)
require.Len(t, store.Calls, 2)
@@ -649,10 +789,33 @@ func TestCreateTemplate(t *testing.T) {
require.ErrorIs(t, err, ErrTemplateInvalid)
})
t.Run("invalid kind", func(t *testing.T) {
tmpl := definitions.NotificationTemplate{
Name: "new-template",
Template: "{{ define \"test\"}} test {{ end }}",
Kind: "unknown",
}
_, err := sut.CreateTemplate(context.Background(), orgID, tmpl)
require.ErrorIs(t, err, ErrTemplateInvalid)
})
require.Empty(t, store.Calls)
prov.AssertExpectations(t)
})
t.Run("rejects templates with mimir kind", func(t *testing.T) {
sut, _, _ := createTemplateServiceSut()
tmpl := definitions.NotificationTemplate{
Name: "new-template",
Template: "{{ define \"test\"}} test {{ end }}",
Kind: definition.MimirTemplateKind,
}
_, err := sut.CreateTemplate(context.Background(), orgID, tmpl)
require.ErrorIs(t, err, ErrTemplateInvalid)
})
t.Run("propagates errors", func(t *testing.T) {
t.Run("when unable to read config", func(t *testing.T) {
sut, store, _ := createTemplateServiceSut()
@@ -706,6 +869,7 @@ func TestUpdateTemplate(t *testing.T) {
Template: "{{ define \"test\"}} test {{ end }}",
Provenance: definitions.Provenance(models.ProvenanceAPI),
ResourceVersion: "",
Kind: definition.GrafanaTemplateKind,
}
amConfigToken := util.GenerateShortUID()
@@ -771,7 +935,7 @@ func TestUpdateTemplate(t *testing.T) {
},
{
name: "by uid",
templateUid: legacy_storage.NameToUid(tmpl.UID),
templateUid: templateUID(tmpl.Kind, tmpl.Name),
},
}
@@ -791,13 +955,12 @@ func TestUpdateTemplate(t *testing.T) {
result, err := sut.UpdateTemplate(context.Background(), orgID, tmpl)
require.NoError(t, err)
assert.Equal(t, definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(tmpl.Name),
Name: tmpl.Name,
Template: tmpl.Template,
Provenance: tmpl.Provenance,
ResourceVersion: calculateTemplateFingerprint(tmpl.Template),
}, result)
assert.Equal(t, newNotificationTemplate(
tmpl.Name,
tmpl.Template,
models.Provenance(tmpl.Provenance),
tmpl.Kind,
), result)
require.Len(t, store.Calls, 2)
require.Equal(t, "Save", store.Calls[1].Method)
@@ -821,13 +984,12 @@ func TestUpdateTemplate(t *testing.T) {
result, err := sut.UpdateTemplate(context.Background(), orgID, tmpl)
require.NoError(t, err)
assert.Equal(t, definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(tmpl.Name),
Name: tmpl.Name,
Template: tmpl.Template,
Provenance: tmpl.Provenance,
ResourceVersion: calculateTemplateFingerprint(tmpl.Template),
}, result)
assert.Equal(t, newNotificationTemplate(
tmpl.Name,
tmpl.Template,
models.Provenance(tmpl.Provenance),
tmpl.Kind,
), result)
require.Equal(t, "Save", store.Calls[1].Method)
saved := store.Calls[1].Args[1].(*legacy_storage.ConfigRevision)
@@ -853,18 +1015,17 @@ func TestUpdateTemplate(t *testing.T) {
oldName := tmpl.Name
tmpl := tmpl
tmpl.UID = legacy_storage.NameToUid(tmpl.Name) // UID matches the current template
tmpl.Name = "new-template-name" // but name is different
tmpl.UID = templateUID(tmpl.Kind, tmpl.Name) // UID matches the current template
tmpl.Name = "new-template-name" // but name is different
result, err := sut.UpdateTemplate(context.Background(), orgID, tmpl)
require.NoError(t, err)
assert.Equal(t, definitions.NotificationTemplate{
UID: legacy_storage.NameToUid(tmpl.Name),
Name: tmpl.Name,
Template: tmpl.Template,
Provenance: tmpl.Provenance,
ResourceVersion: calculateTemplateFingerprint(tmpl.Template),
}, result)
assert.Equal(t, newNotificationTemplate(
tmpl.Name,
tmpl.Template,
models.Provenance(tmpl.Provenance),
tmpl.Kind,
), result)
require.Len(t, store.Calls, 2)
require.Equal(t, "Save", store.Calls[1].Method)
@@ -882,6 +1043,7 @@ func TestUpdateTemplate(t *testing.T) {
t.Run("rejects rename operation if template with the new name exists", func(t *testing.T) {
sut, store, prov := createTemplateServiceSut()
prov.EXPECT().GetProvenance(mock.Anything, mock.Anything, mock.Anything).Return(models.ProvenanceNone, nil)
store.GetFn = func(ctx context.Context, org int64) (*legacy_storage.ConfigRevision, error) {
return &legacy_storage.ConfigRevision{
Config: &definitions.PostableUserConfig{
@@ -895,8 +1057,8 @@ func TestUpdateTemplate(t *testing.T) {
}
tmpl := tmpl
tmpl.UID = legacy_storage.NameToUid(tmpl.Name) // UID matches the current template
tmpl.Name = "new-template-name" // but name matches another existing template
tmpl.UID = templateUID(tmpl.Kind, tmpl.Name) // UID matches the current template
tmpl.Name = "new-template-name" // but name matches another existing template
_, err := sut.UpdateTemplate(context.Background(), orgID, tmpl)
require.ErrorIs(t, err, ErrTemplateExists)
@@ -925,6 +1087,16 @@ func TestUpdateTemplate(t *testing.T) {
require.ErrorIs(t, err, ErrTemplateInvalid)
})
t.Run("invalid kind", func(t *testing.T) {
tmpl := definitions.NotificationTemplate{
Name: "",
Template: "",
Kind: "unknown",
}
_, err := sut.UpdateTemplate(context.Background(), orgID, tmpl)
require.ErrorIs(t, err, ErrTemplateInvalid)
})
require.Empty(t, store.Calls)
prov.AssertExpectations(t)
})
@@ -975,6 +1147,27 @@ func TestUpdateTemplate(t *testing.T) {
prov.AssertExpectations(t)
})
t.Run("rejects existing templates if kind changes", func(t *testing.T) {
sut, store, prov := createTemplateServiceSut()
store.GetFn = func(ctx context.Context, org int64) (*legacy_storage.ConfigRevision, error) {
return revision(), nil
}
prov.EXPECT().GetProvenance(mock.Anything, mock.Anything, mock.Anything).Return(models.ProvenanceNone, nil)
template := definitions.NotificationTemplate{
Name: "template1",
Template: "asdf-new",
ResourceVersion: "bad-version",
Provenance: definitions.Provenance(models.ProvenanceNone),
Kind: definition.MimirTemplateKind,
}
_, err := sut.UpdateTemplate(context.Background(), orgID, template)
require.ErrorIs(t, err, ErrTemplateInvalid)
prov.AssertExpectations(t)
})
t.Run("propagates errors", func(t *testing.T) {
t.Run("when unable to read config", func(t *testing.T) {
sut, store, _ := createTemplateServiceSut()
@@ -1062,7 +1255,7 @@ func TestDeleteTemplate(t *testing.T) {
},
{
name: "by uid",
templateNameOrUid: legacy_storage.NameToUid(templateName),
templateNameOrUid: templateUID(definition.GrafanaTemplateKind, templateName),
},
}
for _, tt := range testCase {
@@ -1125,7 +1318,7 @@ func TestDeleteTemplate(t *testing.T) {
}
t.Run("should look by name before uid", func(t *testing.T) {
expectedToDelete := legacy_storage.NameToUid(templateName)
expectedToDelete := templateUID(definition.GrafanaTemplateKind, templateName)
sut, store, prov := createTemplateServiceSut()
store.GetFn = func(ctx context.Context, orgID int64) (*legacy_storage.ConfigRevision, error) {
return &legacy_storage.ConfigRevision{
+3 -2
View File
@@ -7,6 +7,7 @@ import (
"strconv"
"strings"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/sqlstore/session"
"github.com/grafana/grafana/pkg/util/xorm"
)
@@ -77,7 +78,7 @@ type Dialect interface {
// CreateDatabaseFromSnapshot is called when migration log table is not found.
// Dialect can recreate all tables from existing snapshot. After successful (nil error) return,
// migrator will list migrations from the log, and apply all missing migrations.
CreateDatabaseFromSnapshot(ctx context.Context, engine *xorm.Engine, migrationLogTableName string) error
CreateDatabaseFromSnapshot(ctx context.Context, engine *xorm.Engine, migrationLogTableName string, logger log.Logger) error
IsUniqueConstraintViolation(err error) bool
ErrorMessage(err error) string
@@ -355,7 +356,7 @@ func (b *BaseDialect) CleanDB(engine *xorm.Engine) error {
return nil
}
func (b *BaseDialect) CreateDatabaseFromSnapshot(ctx context.Context, engine *xorm.Engine, tableName string) error {
func (b *BaseDialect) CreateDatabaseFromSnapshot(ctx context.Context, engine *xorm.Engine, migrationLogTableName string, logger log.Logger) error {
return nil
}
+1 -1
View File
@@ -250,7 +250,7 @@ func (mg *Migrator) run(ctx context.Context) (err error) {
if !migrationLogExists {
// Check if dialect can initialize database from a snapshot.
err := mg.Dialect.CreateDatabaseFromSnapshot(ctx, mg.DBEngine, mg.tableName)
err := mg.Dialect.CreateDatabaseFromSnapshot(ctx, mg.DBEngine, mg.tableName, logger)
if err != nil {
return fmt.Errorf("failed to create database from snapshot: %w", err)
}
@@ -1,15 +1,19 @@
package migrator
import (
"context"
"database/sql"
"embed"
"errors"
"fmt"
"path"
"strconv"
"strings"
"github.com/VividCortex/mysqlerr"
"github.com/go-sql-driver/mysql"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/util/xorm"
)
@@ -317,3 +321,87 @@ func (db *MySQLDialect) GetDBName(dsn string) (string, error) {
return cfg.DBName, nil
}
//go:embed snapshot/*.sql
var sqlFiles embed.FS
func (db *MySQLDialect) CreateDatabaseFromSnapshot(ctx context.Context, engine *xorm.Engine, migrationLogTableName string, logger log.Logger) error {
// Entire schema is generated only when called with "migration_log" table.
// Normally if schema creates other "*_migration_log" tables, we don't get here. However if new migration table is introduced by later migration,
// CreateDatabaseFromSnapshot may be called for given table, but if it wasn't part of original schema, we can't do anything here.
if migrationLogTableName != "migration_log" {
return nil
}
entries, err := sqlFiles.ReadDir("snapshot")
if err != nil {
return err
}
logger.Info("creating database schema from snapshot")
for _, entry := range entries {
logger.Debug("using snapshot file", "file", entry.Name())
data, err := sqlFiles.ReadFile(path.Join("snapshot", entry.Name()))
if err != nil {
return err
}
statements := extractStatements(string(data))
if err := db.executeStatements(engine, statements); err != nil {
return err
}
}
logger.Info("successfully created database schema from snapshot")
return nil
}
func extractStatements(schema string) []string {
lines := strings.Split(schema, "\n")
statements := make([]string, 0, len(lines))
sb := strings.Builder{}
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "--") {
continue
}
if line == "" {
continue
}
endOfStatement := false
if strings.HasSuffix(line, ";") {
endOfStatement = true
line = strings.TrimSuffix(line, ";")
}
if sb.Len() > 0 {
sb.WriteRune('\n')
}
sb.WriteString(line)
if endOfStatement && sb.Len() > 0 {
statements = append(statements, sb.String())
sb.Reset()
}
}
if sb.Len() > 0 {
statements = append(statements, sb.String())
}
return statements
}
func (s *MySQLDialect) executeStatements(engine *xorm.Engine, statements []string) error {
sess := engine.NewSession()
for _, s := range statements {
_, err := sess.Exec(s)
if err != nil {
return fmt.Errorf("statement %s failed with error: %v", s, err)
}
}
return nil
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,57 @@
-- MySQL dump 10.13 Distrib 8.4.5, for Linux (x86_64)
--
-- Host: localhost Database: hg_dump
-- ------------------------------------------------------
-- Server version 8.4.5
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Dumping data for table `secret_migration_log`
--
INSERT INTO `secret_migration_log` (`migration_id`, `sql`, `success`, `error`, `timestamp`) VALUES
('create secret_migration_log table','CREATE TABLE IF NOT EXISTS `secret_migration_log` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `migration_id` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `sql` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `success` TINYINT(1) NOT NULL\n, `error` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `timestamp` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Initialize secrets tables','',1,'','2022-01-01 00:00:00'),
('drop table secret_secure_value','DROP TABLE IF EXISTS `secret_secure_value`',1,'','2022-01-01 00:00:00'),
('create table secret_secure_value','CREATE TABLE IF NOT EXISTS `secret_secure_value` (\n`guid` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `name` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `namespace` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `annotations` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `labels` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` BIGINT(20) NOT NULL\n, `created_by` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `updated` BIGINT(20) NOT NULL\n, `updated_by` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `external_id` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `active` TINYINT(1) NOT NULL\n, `version` BIGINT(20) NOT NULL\n, `description` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `keeper` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `decrypters` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `ref` VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create table secret_secure_value, index: 0','CREATE UNIQUE INDEX `UQE_secret_secure_value_namespace_name_version_active` ON `secret_secure_value` (`namespace`,`name`,`version`,`active`);',1,'','2022-01-01 00:00:00'),
('create table secret_secure_value, index: 1','CREATE UNIQUE INDEX `UQE_secret_secure_value_namespace_name_version` ON `secret_secure_value` (`namespace`,`name`,`version`);',1,'','2022-01-01 00:00:00'),
('drop table secret_keeper','DROP TABLE IF EXISTS `secret_keeper`',1,'','2022-01-01 00:00:00'),
('create table secret_keeper','CREATE TABLE IF NOT EXISTS `secret_keeper` (\n`guid` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `name` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `namespace` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `annotations` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `labels` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` BIGINT(20) NOT NULL\n, `created_by` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `updated` BIGINT(20) NOT NULL\n, `updated_by` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `description` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `type` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `payload` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create table secret_keeper, index: 0','CREATE UNIQUE INDEX `UQE_secret_keeper_namespace_name` ON `secret_keeper` (`namespace`,`name`);',1,'','2022-01-01 00:00:00'),
('drop table secret_data_key','DROP TABLE IF EXISTS `secret_data_key`',1,'','2022-01-01 00:00:00'),
('create table secret_data_key','CREATE TABLE IF NOT EXISTS `secret_data_key` (\n`uid` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `namespace` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `label` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `active` TINYINT(1) NOT NULL\n, `provider` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `encrypted_data` BLOB NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('drop table secret_encrypted_value','DROP TABLE IF EXISTS `secret_encrypted_value`',1,'','2022-01-01 00:00:00'),
('create table secret_encrypted_value','CREATE TABLE IF NOT EXISTS `secret_encrypted_value` (\n`namespace` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `version` BIGINT(20) NOT NULL\n, `encrypted_data` BLOB NOT NULL\n, `created` BIGINT(20) NOT NULL\n, `updated` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create table secret_encrypted_value, index: 0','CREATE UNIQUE INDEX `UQE_secret_encrypted_value_namespace_name_version` ON `secret_encrypted_value` (`namespace`,`name`,`version`);',1,'','2022-01-01 00:00:00'),
('create index for list on secret_secure_value','CREATE INDEX `IDX_secret_secure_value_namespace_active_updated` ON `secret_secure_value` (`namespace`,`active`,`updated`);',1,'','2022-01-01 00:00:00'),
('create index for list and read current on secret_data_key','CREATE INDEX `IDX_secret_data_key_namespace_label_active` ON `secret_data_key` (`namespace`,`label`,`active`);',1,'','2022-01-01 00:00:00'),
('add owner_reference_api_group column to secret_secure_value','alter table `secret_secure_value` ADD COLUMN `owner_reference_api_group` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add owner_reference_api_version column to secret_secure_value','alter table `secret_secure_value` ADD COLUMN `owner_reference_api_version` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add owner_reference_kind column to secret_secure_value','alter table `secret_secure_value` ADD COLUMN `owner_reference_kind` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add owner_reference_name column to secret_secure_value','alter table `secret_secure_value` ADD COLUMN `owner_reference_name` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add lease_token column to secret_secure_value','alter table `secret_secure_value` ADD COLUMN `lease_token` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add lease_token index to secret_secure_value','CREATE INDEX `IDX_secret_secure_value_lease_token` ON `secret_secure_value` (`lease_token`);',1,'','2022-01-01 00:00:00'),
('add lease_created column to secret_secure_value','alter table `secret_secure_value` ADD COLUMN `lease_created` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add lease_created index to secret_secure_value','CREATE INDEX `IDX_secret_secure_value_lease_created` ON `secret_secure_value` (`lease_created`);',1,'','2022-01-01 00:00:00'),
('add data_key_id column to secret_encrypted_value','alter table `secret_encrypted_value` ADD COLUMN `data_key_id` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('add data_key_id index to secret_encrypted_value','CREATE INDEX `IDX_secret_encrypted_value_data_key_id` ON `secret_encrypted_value` (`data_key_id`);',1,'','2022-01-01 00:00:00'),
('add active column to secret_keeper','alter table `secret_keeper` ADD COLUMN `active` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add active column index to secret_keeper','CREATE INDEX `IDX_secret_keeper_namespace_name_active` ON `secret_keeper` (`namespace`,`name`,`active`);',1,'','2022-01-01 00:00:00'),
('set secret_secure_value.keeper to \'system\' where keeper is null in secret_secure_value','UPDATE secret_secure_value SET keeper = \'system\' WHERE keeper IS NULL',1,'','2022-01-01 00:00:00'),
('drop my_row_id and add primary key with columns namespace,name,version to table secret_encrypted_value if my_row_id exists (auto-generated mysql column)','\n ALTER TABLE secret_encrypted_value\n DROP PRIMARY KEY,\n DROP COLUMN my_row_id,\n DROP INDEX UQE_secret_encrypted_value_namespace_name_version,\n ADD PRIMARY KEY (`namespace`,`name`,`version`);\n ',1,'','2022-01-01 00:00:00'),
('drop unique index UQE_secret_encrypted_value_namespace_name_version from secret_encrypted_value table if it exists (mysql)','ALTER TABLE secret_encrypted_value DROP INDEX UQE_secret_encrypted_value_namespace_name_version',1,'','2022-01-01 00:00:00'),
('add primary key with columns namespace,name,version to table secret_encrypted_value if it doesn\'t exist (mysql)','ALTER TABLE secret_encrypted_value ADD PRIMARY KEY (`namespace`,`name`,`version`)',1,'','2022-01-01 00:00:00'),
('add primary key with columns namespace,name,version to table secret_encrypted_value (postgres and sqlite)','',1,'','2022-01-01 00:00:00');
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
@@ -0,0 +1,60 @@
-- MySQL dump 10.13 Distrib 8.4.5, for Linux (x86_64)
--
-- Host: localhost Database: hg_dump
-- ------------------------------------------------------
-- Server version 8.4.5
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Dumping data for table `resource_migration_log`
--
INSERT INTO `resource_migration_log` (`migration_id`, `sql`, `success`, `error`, `timestamp`) VALUES
('create resource_migration_log table','CREATE TABLE IF NOT EXISTS `resource_migration_log` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `migration_id` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `sql` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `success` TINYINT(1) NOT NULL\n, `error` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `timestamp` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Initialize resource tables','',1,'','2022-01-01 00:00:00'),
('drop table resource','DROP TABLE IF EXISTS `resource`',1,'','2022-01-01 00:00:00'),
('create table resource','CREATE TABLE IF NOT EXISTS `resource` (\n`guid` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `resource_version` BIGINT(20) NULL\n, `group` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `resource` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `namespace` VARCHAR(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `value` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `action` INT NOT NULL\n, `label_set` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create table resource, index: 0','CREATE UNIQUE INDEX `UQE_resource_namespace_group_resource_name` ON `resource` (`namespace`,`group`,`resource`,`name`);',1,'','2022-01-01 00:00:00'),
('drop table resource_history','DROP TABLE IF EXISTS `resource_history`',1,'','2022-01-01 00:00:00'),
('create table resource_history','CREATE TABLE IF NOT EXISTS `resource_history` (\n`guid` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `resource_version` BIGINT(20) NULL\n, `group` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `resource` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `namespace` VARCHAR(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `value` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `action` INT NOT NULL\n, `label_set` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create table resource_history, index: 0','CREATE UNIQUE INDEX `UQE_resource_history_namespace_group_name_version` ON `resource_history` (`namespace`,`group`,`resource`,`name`,`resource_version`);',1,'','2022-01-01 00:00:00'),
('create table resource_history, index: 1','CREATE INDEX `IDX_resource_history_resource_version` ON `resource_history` (`resource_version`);',1,'','2022-01-01 00:00:00'),
('drop table resource_version','DROP TABLE IF EXISTS `resource_version`',1,'','2022-01-01 00:00:00'),
('create table resource_version','CREATE TABLE IF NOT EXISTS `resource_version` (\n`group` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `resource` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `resource_version` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create table resource_version, index: 0','CREATE UNIQUE INDEX `UQE_resource_version_group_resource` ON `resource_version` (`group`,`resource`);',1,'','2022-01-01 00:00:00'),
('drop table resource_blob','DROP TABLE IF EXISTS `resource_blob`',1,'','2022-01-01 00:00:00'),
('create table resource_blob','CREATE TABLE IF NOT EXISTS `resource_blob` (\n`uuid` CHAR(36) PRIMARY KEY NOT NULL\n, `created` DATETIME NOT NULL\n, `group` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `resource` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `namespace` VARCHAR(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `value` LONGBLOB NOT NULL\n, `hash` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `content_type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create table resource_blob, index: 0','CREATE INDEX `IDX_resource_history_namespace_group_name` ON `resource_blob` (`namespace`,`group`,`resource`,`name`);',1,'','2022-01-01 00:00:00'),
('create table resource_blob, index: 1','CREATE INDEX `IDX_resource_blob_created` ON `resource_blob` (`created`);',1,'','2022-01-01 00:00:00'),
('drop table resource_last_import_time','DROP TABLE IF EXISTS `resource_last_import_time`',1,'','2022-01-01 00:00:00'),
('create table resource_last_import_time','CREATE TABLE IF NOT EXISTS `resource_last_import_time` (\n`group` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `resource` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `namespace` VARCHAR(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `last_import_time` DATETIME NOT NULL\n, PRIMARY KEY ( `group`,`resource`,`namespace` )) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add column previous_resource_version in resource_history','alter table `resource_history` ADD COLUMN `previous_resource_version` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('Add column previous_resource_version in resource','alter table `resource` ADD COLUMN `previous_resource_version` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('Add index to resource_history for polling','CREATE INDEX `IDX_resource_history_group_resource_resource_version` ON `resource_history` (`group`,`resource`,`resource_version`);',1,'','2022-01-01 00:00:00'),
('Add index to resource for loading','CREATE INDEX `IDX_resource_group_resource` ON `resource` (`group`,`resource`);',1,'','2022-01-01 00:00:00'),
('Add column folder in resource_history','alter table `resource_history` ADD COLUMN `folder` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('Add column folder in resource','alter table `resource` ADD COLUMN `folder` VARCHAR(253) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('Migrate DeletionMarkers to real Resource objects','Find rows in resource_history with value LIKE {\"kind\":\"DeletedMarker\"%',1,'','2022-01-01 00:00:00'),
('Add index to resource_history for get trash','CREATE INDEX `IDX_resource_history_namespace_group_resource_action_version` ON `resource_history` (`namespace`,`group`,`resource`,`action`,`resource_version`);',1,'','2022-01-01 00:00:00'),
('Add generation to resource history','alter table `resource_history` ADD COLUMN `generation` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add generation index to resource history','CREATE INDEX `IDX_resource_history_namespace_group_resource_name_generation` ON `resource_history` (`namespace`,`group`,`resource`,`name`,`generation`);',1,'','2022-01-01 00:00:00'),
('Add UQE_resource_last_import_time_last_import_time index','CREATE INDEX `UQE_resource_last_import_time_last_import_time` ON `resource_last_import_time` (`last_import_time`);',1,'','2022-01-01 00:00:00'),
('Add key_path column to resource_history','alter table `resource_history` ADD COLUMN `key_path` VARCHAR(2048) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('create table resource_events','CREATE TABLE IF NOT EXISTS `resource_events` (\n`key_path` VARCHAR(2048) CHARACTER SET latin1 COLLATE latin1_bin PRIMARY KEY NOT NULL\n, `value` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add IDX_resource_history_key_path index','CREATE INDEX `IDX_resource_history_key_path` ON `resource_history` (`key_path`);',1,'','2022-01-01 00:00:00'),
('drop my_row_id and add primary key with columns group,resource to table resource_version if my_row_id exists (auto-generated mysql column)','\n ALTER TABLE resource_version\n DROP PRIMARY KEY,\n DROP COLUMN my_row_id,\n DROP INDEX UQE_resource_version_group_resource,\n ADD PRIMARY KEY (`group`,`resource`);\n ',1,'','2022-01-01 00:00:00'),
('drop unique index UQE_resource_version_group_resource from resource_version table if it exists (mysql)','ALTER TABLE resource_version DROP INDEX UQE_resource_version_group_resource',1,'','2022-01-01 00:00:00'),
('add primary key with columns group,resource to table resource_version if it doesn\'t exist (mysql)','ALTER TABLE resource_version ADD PRIMARY KEY (`group`,`resource`)',1,'','2022-01-01 00:00:00'),
('add primary key with columns group,resource to table resource_version (postgres and sqlite)','',1,'','2022-01-01 00:00:00');
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
@@ -0,0 +1,469 @@
-- MySQL dump 10.13 Distrib 8.4.5, for Linux (x86_64)
--
-- Host: localhost Database: hg_dump
-- ------------------------------------------------------
-- Server version 8.4.5
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Dumping data for table `alert`
--
--
-- Dumping data for table `alert_image`
--
--
-- Dumping data for table `alert_instance`
--
--
-- Dumping data for table `alert_notification`
--
--
-- Dumping data for table `alert_notification_state`
--
--
-- Dumping data for table `alert_rule`
--
--
-- Dumping data for table `alert_rule_state`
--
--
-- Dumping data for table `alert_rule_tag`
--
--
-- Dumping data for table `alert_rule_version`
--
--
-- Dumping data for table `annotation`
--
--
-- Dumping data for table `annotation_tag`
--
--
-- Dumping data for table `anon_device`
--
--
-- Dumping data for table `api_key`
--
--
-- Dumping data for table `cache_data`
--
--
-- Dumping data for table `cloud_migration_resource`
--
--
-- Dumping data for table `cloud_migration_session`
--
--
-- Dumping data for table `cloud_migration_snapshot`
--
--
-- Dumping data for table `cloud_migration_snapshot_partition`
--
--
-- Dumping data for table `correlation`
--
--
-- Dumping data for table `dashboard`
--
--
-- Dumping data for table `dashboard_acl`
--
INSERT INTO `dashboard_acl` (`id`, `org_id`, `dashboard_id`, `user_id`, `team_id`, `permission`, `role`, `created`, `updated`) VALUES
(1,-1,-1,NULL,NULL,1,'Viewer','2022-01-01 00:00:00','2022-01-01 00:00:00'),
(2,-1,-1,NULL,NULL,2,'Editor','2022-01-01 00:00:00','2022-01-01 00:00:00');
--
-- Dumping data for table `dashboard_provisioning`
--
--
-- Dumping data for table `dashboard_public`
--
--
-- Dumping data for table `dashboard_public_email_share`
--
--
-- Dumping data for table `dashboard_public_magic_link`
--
--
-- Dumping data for table `dashboard_public_session`
--
--
-- Dumping data for table `dashboard_public_usage_by_day`
--
--
-- Dumping data for table `dashboard_snapshot`
--
--
-- Dumping data for table `dashboard_tag`
--
--
-- Dumping data for table `dashboard_usage_by_day`
--
--
-- Dumping data for table `dashboard_usage_sums`
--
--
-- Dumping data for table `dashboard_version`
--
--
-- Dumping data for table `data_keys`
--
--
-- Dumping data for table `data_source`
--
--
-- Dumping data for table `data_source_acl`
--
--
-- Dumping data for table `data_source_cache`
--
--
-- Dumping data for table `data_source_usage_by_day`
--
--
-- Dumping data for table `entity_event`
--
--
-- Dumping data for table `file`
--
--
-- Dumping data for table `file_meta`
--
--
-- Dumping data for table `folder`
--
--
-- Dumping data for table `library_element`
--
--
-- Dumping data for table `library_element_connection`
--
--
-- Dumping data for table `license_token`
--
--
-- Dumping data for table `login_attempt`
--
--
-- Dumping data for table `ngalert_configuration`
--
--
-- Dumping data for table `playlist`
--
--
-- Dumping data for table `playlist_item`
--
--
-- Dumping data for table `plugin_setting`
--
--
-- Dumping data for table `preferences`
--
--
-- Dumping data for table `provenance_type`
--
--
-- Dumping data for table `query_history`
--
--
-- Dumping data for table `query_history_details`
--
--
-- Dumping data for table `query_history_star`
--
--
-- Dumping data for table `quota`
--
--
-- Dumping data for table `recording_rules`
--
--
-- Dumping data for table `remote_write_targets`
--
--
-- Dumping data for table `report`
--
--
-- Dumping data for table `report_dashboards`
--
--
-- Dumping data for table `report_settings`
--
--
-- Dumping data for table `resource`
--
--
-- Dumping data for table `resource_blob`
--
--
-- Dumping data for table `resource_events`
--
--
-- Dumping data for table `resource_history`
--
--
-- Dumping data for table `secret_data_key`
--
--
-- Dumping data for table `secret_encrypted_value`
--
--
-- Dumping data for table `secret_keeper`
--
--
-- Dumping data for table `secret_secure_value`
--
--
-- Dumping data for table `secrets`
--
--
-- Dumping data for table `session`
--
--
-- Dumping data for table `setting`
--
--
-- Dumping data for table `short_url`
--
--
-- Dumping data for table `signing_key`
--
--
-- Dumping data for table `sso_setting`
--
--
-- Dumping data for table `star`
--
--
-- Dumping data for table `tag`
--
--
-- Dumping data for table `team`
--
--
-- Dumping data for table `team_group`
--
--
-- Dumping data for table `team_member`
--
--
-- Dumping data for table `team_role`
--
--
-- Dumping data for table `temp_user`
--
--
-- Dumping data for table `test_data`
--
--
-- Dumping data for table `unifiedstorage_migration_log`
--
INSERT INTO `unifiedstorage_migration_log` (`id`, `migration_id`, `sql`, `success`, `error`, `timestamp`) VALUES
(1,'create unifiedstorage_migration_log table','CREATE TABLE IF NOT EXISTS `unifiedstorage_migration_log` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `migration_id` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `sql` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `success` TINYINT(1) NOT NULL\n, `error` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `timestamp` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
(2,'playlists migration','unified storage data migration: playlists',1,'','2022-01-01 00:00:00');
--
-- Dumping data for table `user_auth`
--
--
-- Dumping data for table `user_auth_token`
--
--
-- Dumping data for table `user_dashboard_views`
--
--
-- Dumping data for table `user_external_session`
--
--
-- Dumping data for table `user_role`
--
--
-- Dumping data for table `user_stats`
--
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
@@ -0,0 +1,850 @@
-- MySQL dump 10.13 Distrib 8.4.5, for Linux (x86_64)
--
-- Host: localhost Database: hg_dump
-- ------------------------------------------------------
-- Server version 8.4.5
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Dumping data for table `migration_log`
--
INSERT INTO `migration_log` (`migration_id`, `sql`, `success`, `error`, `timestamp`) VALUES
('create migration_log table','CREATE TABLE IF NOT EXISTS `migration_log` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `migration_id` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `sql` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `success` TINYINT(1) NOT NULL\n, `error` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `timestamp` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create user table','CREATE TABLE IF NOT EXISTS `user` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `version` INT NOT NULL\n, `login` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `email` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `salt` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `rands` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `company` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `account_id` BIGINT(20) NOT NULL\n, `is_admin` TINYINT(1) NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index user.login','CREATE UNIQUE INDEX `UQE_user_login` ON `user` (`login`);',1,'','2022-01-01 00:00:00'),
('add unique index user.email','CREATE UNIQUE INDEX `UQE_user_email` ON `user` (`email`);',1,'','2022-01-01 00:00:00'),
('drop index UQE_user_login - v1','DROP INDEX `UQE_user_login` ON `user`',1,'','2022-01-01 00:00:00'),
('drop index UQE_user_email - v1','DROP INDEX `UQE_user_email` ON `user`',1,'','2022-01-01 00:00:00'),
('Rename table user to user_v1 - v1','ALTER TABLE `user` RENAME TO `user_v1`',1,'','2022-01-01 00:00:00'),
('create user table v2','CREATE TABLE IF NOT EXISTS `user` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `version` INT NOT NULL\n, `login` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `email` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `salt` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `rands` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `company` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `org_id` BIGINT(20) NOT NULL\n, `is_admin` TINYINT(1) NOT NULL\n, `email_verified` TINYINT(1) NULL\n, `theme` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_user_login - v2','CREATE UNIQUE INDEX `UQE_user_login` ON `user` (`login`);',1,'','2022-01-01 00:00:00'),
('create index UQE_user_email - v2','CREATE UNIQUE INDEX `UQE_user_email` ON `user` (`email`);',1,'','2022-01-01 00:00:00'),
('copy data_source v1 to v2','INSERT INTO `user` (`version`\n, `email`\n, `name`\n, `password`\n, `is_admin`\n, `id`\n, `login`\n, `salt`\n, `rands`\n, `company`\n, `org_id`\n, `created`\n, `updated`)\nSELECT `version`\n, `email`\n, `name`\n, `password`\n, `is_admin`\n, `id`\n, `login`\n, `salt`\n, `rands`\n, `company`\n, `account_id`\n, `created`\n, `updated`\nFROM `user_v1`',1,'','2022-01-01 00:00:00'),
('Drop old table user_v1','DROP TABLE IF EXISTS `user_v1`',1,'','2022-01-01 00:00:00'),
('Add column help_flags1 to user table','alter table `user` ADD COLUMN `help_flags1` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Update user table charset','ALTER TABLE `user` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `login` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `email` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `salt` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `rands` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `company` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `theme` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ;',1,'','2022-01-01 00:00:00'),
('Add last_seen_at column to user','alter table `user` ADD COLUMN `last_seen_at` DATETIME NULL ',1,'','2022-01-01 00:00:00'),
('Add missing user data','code migration',1,'','2022-01-01 00:00:00'),
('Add is_disabled column to user','alter table `user` ADD COLUMN `is_disabled` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add index user.login/user.email','CREATE INDEX `IDX_user_login_email` ON `user` (`login`,`email`);',1,'','2022-01-01 00:00:00'),
('Add is_service_account column to user','alter table `user` ADD COLUMN `is_service_account` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Update is_service_account column to nullable','ALTER TABLE user MODIFY is_service_account BOOLEAN DEFAULT 0;',1,'','2022-01-01 00:00:00'),
('Add uid column to user','alter table `user` ADD COLUMN `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update uid column values for users','UPDATE user SET uid=concat(\'u\',lpad(id,9,\'0\')) WHERE uid IS NULL;',1,'','2022-01-01 00:00:00'),
('Make sure users uid are set','UPDATE user SET uid=concat(\'u\',lpad(id,9,\'0\')) WHERE uid is NULL OR uid = \'\';',1,'','2022-01-01 00:00:00'),
('Add unique index user_uid','CREATE UNIQUE INDEX `UQE_user_uid` ON `user` (`uid`);',1,'','2022-01-01 00:00:00'),
('Add is_provisioned column to user','alter table `user` ADD COLUMN `is_provisioned` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('update login field with orgid to allow for multiple service accounts with same name across orgs','code migration',1,'','2022-01-01 00:00:00'),
('update service accounts login field orgid to appear only once','code migration',1,'','2022-01-01 00:00:00'),
('update login and email fields to lowercase','code migration',1,'','2022-01-01 00:00:00'),
('update login and email fields to lowercase2','code migration',1,'','2022-01-01 00:00:00'),
('Add index on user.is_service_account and user.last_seen_at','CREATE INDEX `IDX_user_is_service_account_last_seen_at` ON `user` (`is_service_account`,`last_seen_at`);',1,'','2022-01-01 00:00:00'),
('Expand user.uid length to 190','ALTER TABLE user MODIFY uid VARCHAR(190);',1,'','2022-01-01 00:00:00'),
('Prefix SCIM uid for provisioned users','UPDATE user SET uid = CONCAT(\'scim-\', uid) WHERE is_provisioned = 1 AND uid NOT LIKE \'scim-%\';',1,'','2022-01-01 00:00:00'),
('create temp user table v1-7','CREATE TABLE IF NOT EXISTS `temp_user` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `version` INT NOT NULL\n, `email` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `role` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `code` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `status` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `invited_by_user_id` BIGINT(20) NULL\n, `email_sent` TINYINT(1) NOT NULL\n, `email_sent_on` DATETIME NULL\n, `remote_addr` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index IDX_temp_user_email - v1-7','CREATE INDEX `IDX_temp_user_email` ON `temp_user` (`email`);',1,'','2022-01-01 00:00:00'),
('create index IDX_temp_user_org_id - v1-7','CREATE INDEX `IDX_temp_user_org_id` ON `temp_user` (`org_id`);',1,'','2022-01-01 00:00:00'),
('create index IDX_temp_user_code - v1-7','CREATE INDEX `IDX_temp_user_code` ON `temp_user` (`code`);',1,'','2022-01-01 00:00:00'),
('create index IDX_temp_user_status - v1-7','CREATE INDEX `IDX_temp_user_status` ON `temp_user` (`status`);',1,'','2022-01-01 00:00:00'),
('Update temp_user table charset','ALTER TABLE `temp_user` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `email` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `role` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `code` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `status` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `remote_addr` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ;',1,'','2022-01-01 00:00:00'),
('drop index IDX_temp_user_email - v1','DROP INDEX `IDX_temp_user_email` ON `temp_user`',1,'','2022-01-01 00:00:00'),
('drop index IDX_temp_user_org_id - v1','DROP INDEX `IDX_temp_user_org_id` ON `temp_user`',1,'','2022-01-01 00:00:00'),
('drop index IDX_temp_user_code - v1','DROP INDEX `IDX_temp_user_code` ON `temp_user`',1,'','2022-01-01 00:00:00'),
('drop index IDX_temp_user_status - v1','DROP INDEX `IDX_temp_user_status` ON `temp_user`',1,'','2022-01-01 00:00:00'),
('Rename table temp_user to temp_user_tmp_qwerty - v1','ALTER TABLE `temp_user` RENAME TO `temp_user_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('create temp_user v2','CREATE TABLE IF NOT EXISTS `temp_user` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `version` INT NOT NULL\n, `email` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `role` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `code` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `status` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `invited_by_user_id` BIGINT(20) NULL\n, `email_sent` TINYINT(1) NOT NULL\n, `email_sent_on` DATETIME NULL\n, `remote_addr` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` INT NOT NULL DEFAULT 0\n, `updated` INT NOT NULL DEFAULT 0\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index IDX_temp_user_email - v2','CREATE INDEX `IDX_temp_user_email` ON `temp_user` (`email`);',1,'','2022-01-01 00:00:00'),
('create index IDX_temp_user_org_id - v2','CREATE INDEX `IDX_temp_user_org_id` ON `temp_user` (`org_id`);',1,'','2022-01-01 00:00:00'),
('create index IDX_temp_user_code - v2','CREATE INDEX `IDX_temp_user_code` ON `temp_user` (`code`);',1,'','2022-01-01 00:00:00'),
('create index IDX_temp_user_status - v2','CREATE INDEX `IDX_temp_user_status` ON `temp_user` (`status`);',1,'','2022-01-01 00:00:00'),
('copy temp_user v1 to v2','INSERT INTO `temp_user` (`invited_by_user_id`\n, `email_sent`\n, `email_sent_on`\n, `org_id`\n, `version`\n, `email`\n, `name`\n, `code`\n, `status`\n, `remote_addr`\n, `id`\n, `role`)\nSELECT `invited_by_user_id`\n, `email_sent`\n, `email_sent_on`\n, `org_id`\n, `version`\n, `email`\n, `name`\n, `code`\n, `status`\n, `remote_addr`\n, `id`\n, `role`\nFROM `temp_user_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('drop temp_user_tmp_qwerty','DROP TABLE IF EXISTS `temp_user_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('Set created for temp users that will otherwise prematurely expire','code migration',1,'','2022-01-01 00:00:00'),
('create star table','CREATE TABLE IF NOT EXISTS `star` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `dashboard_id` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index star.user_id_dashboard_id','CREATE UNIQUE INDEX `UQE_star_user_id_dashboard_id` ON `star` (`user_id`,`dashboard_id`);',1,'','2022-01-01 00:00:00'),
('Add column dashboard_uid in star','alter table `star` ADD COLUMN `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add column org_id in star','alter table `star` ADD COLUMN `org_id` BIGINT(20) NULL DEFAULT 1 ',1,'','2022-01-01 00:00:00'),
('Add column updated in star','alter table `star` ADD COLUMN `updated` DATETIME NULL ',1,'','2022-01-01 00:00:00'),
('add index in star table on dashboard_uid, org_id and user_id columns','CREATE UNIQUE INDEX `UQE_star_user_id_dashboard_uid_org_id` ON `star` (`user_id`,`dashboard_uid`,`org_id`);',1,'','2022-01-01 00:00:00'),
('create org table v1','CREATE TABLE IF NOT EXISTS `org` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `version` INT NOT NULL\n, `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `address1` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `address2` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `city` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `state` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `zip_code` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `country` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `billing_email` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_org_name - v1','CREATE UNIQUE INDEX `UQE_org_name` ON `org` (`name`);',1,'','2022-01-01 00:00:00'),
('create org_user table v1','CREATE TABLE IF NOT EXISTS `org_user` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `role` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index IDX_org_user_org_id - v1','CREATE INDEX `IDX_org_user_org_id` ON `org_user` (`org_id`);',1,'','2022-01-01 00:00:00'),
('create index UQE_org_user_org_id_user_id - v1','CREATE UNIQUE INDEX `UQE_org_user_org_id_user_id` ON `org_user` (`org_id`,`user_id`);',1,'','2022-01-01 00:00:00'),
('create index IDX_org_user_user_id - v1','CREATE INDEX `IDX_org_user_user_id` ON `org_user` (`user_id`);',1,'','2022-01-01 00:00:00'),
('Update org table charset','ALTER TABLE `org` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `address1` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `address2` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `city` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `state` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `zip_code` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `country` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `billing_email` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ;',1,'','2022-01-01 00:00:00'),
('Update org_user table charset','ALTER TABLE `org_user` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `role` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Migrate all Read Only Viewers to Viewers','UPDATE org_user SET role = \'Viewer\' WHERE role = \'Read Only Editor\'',1,'','2022-01-01 00:00:00'),
('create dashboard table','CREATE TABLE IF NOT EXISTS `dashboard` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `version` INT NOT NULL\n, `slug` VARCHAR(189) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `account_id` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index dashboard.account_id','CREATE INDEX `IDX_dashboard_account_id` ON `dashboard` (`account_id`);',1,'','2022-01-01 00:00:00'),
('add unique index dashboard_account_id_slug','CREATE UNIQUE INDEX `UQE_dashboard_account_id_slug` ON `dashboard` (`account_id`,`slug`);',1,'','2022-01-01 00:00:00'),
('create dashboard_tag table','CREATE TABLE IF NOT EXISTS `dashboard_tag` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `dashboard_id` BIGINT(20) NOT NULL\n, `term` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index dashboard_tag.dasboard_id_term','CREATE UNIQUE INDEX `UQE_dashboard_tag_dashboard_id_term` ON `dashboard_tag` (`dashboard_id`,`term`);',1,'','2022-01-01 00:00:00'),
('drop index UQE_dashboard_tag_dashboard_id_term - v1','DROP INDEX `UQE_dashboard_tag_dashboard_id_term` ON `dashboard_tag`',1,'','2022-01-01 00:00:00'),
('Rename table dashboard to dashboard_v1 - v1','ALTER TABLE `dashboard` RENAME TO `dashboard_v1`',1,'','2022-01-01 00:00:00'),
('create dashboard v2','CREATE TABLE IF NOT EXISTS `dashboard` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `version` INT NOT NULL\n, `slug` VARCHAR(189) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index IDX_dashboard_org_id - v2','CREATE INDEX `IDX_dashboard_org_id` ON `dashboard` (`org_id`);',1,'','2022-01-01 00:00:00'),
('create index UQE_dashboard_org_id_slug - v2','CREATE UNIQUE INDEX `UQE_dashboard_org_id_slug` ON `dashboard` (`org_id`,`slug`);',1,'','2022-01-01 00:00:00'),
('copy dashboard v1 to v2','INSERT INTO `dashboard` (`id`\n, `version`\n, `slug`\n, `title`\n, `data`\n, `org_id`\n, `created`\n, `updated`)\nSELECT `id`\n, `version`\n, `slug`\n, `title`\n, `data`\n, `account_id`\n, `created`\n, `updated`\nFROM `dashboard_v1`',1,'','2022-01-01 00:00:00'),
('drop table dashboard_v1','DROP TABLE IF EXISTS `dashboard_v1`',1,'','2022-01-01 00:00:00'),
('alter dashboard.data to mediumtext v1','ALTER TABLE dashboard MODIFY data MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('Add column updated_by in dashboard - v2','alter table `dashboard` ADD COLUMN `updated_by` INT NULL ',1,'','2022-01-01 00:00:00'),
('Add column created_by in dashboard - v2','alter table `dashboard` ADD COLUMN `created_by` INT NULL ',1,'','2022-01-01 00:00:00'),
('Add column gnetId in dashboard','alter table `dashboard` ADD COLUMN `gnet_id` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('Add index for gnetId in dashboard','CREATE INDEX `IDX_dashboard_gnet_id` ON `dashboard` (`gnet_id`);',1,'','2022-01-01 00:00:00'),
('Add column plugin_id in dashboard','alter table `dashboard` ADD COLUMN `plugin_id` VARCHAR(189) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add index for plugin_id in dashboard','CREATE INDEX `IDX_dashboard_org_id_plugin_id` ON `dashboard` (`org_id`,`plugin_id`);',1,'','2022-01-01 00:00:00'),
('Add index for dashboard_id in dashboard_tag','CREATE INDEX `IDX_dashboard_tag_dashboard_id` ON `dashboard_tag` (`dashboard_id`);',1,'','2022-01-01 00:00:00'),
('Update dashboard table charset','ALTER TABLE `dashboard` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `slug` VARCHAR(189) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `plugin_id` VARCHAR(189) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `data` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Update dashboard_tag table charset','ALTER TABLE `dashboard_tag` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `term` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Add column folder_id in dashboard','alter table `dashboard` ADD COLUMN `folder_id` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add column isFolder in dashboard','alter table `dashboard` ADD COLUMN `is_folder` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add column has_acl in dashboard','alter table `dashboard` ADD COLUMN `has_acl` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add column uid in dashboard','alter table `dashboard` ADD COLUMN `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update uid column values in dashboard','UPDATE dashboard SET uid=lpad(id,9,\'0\') WHERE uid IS NULL;',1,'','2022-01-01 00:00:00'),
('Add unique index dashboard_org_id_uid','CREATE UNIQUE INDEX `UQE_dashboard_org_id_uid` ON `dashboard` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('Remove unique index org_id_slug','DROP INDEX `UQE_dashboard_org_id_slug` ON `dashboard`',1,'','2022-01-01 00:00:00'),
('Update dashboard title length','ALTER TABLE `dashboard` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `title` VARCHAR(189) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Add unique index for dashboard_org_id_title_folder_id','CREATE UNIQUE INDEX `UQE_dashboard_org_id_folder_id_title` ON `dashboard` (`org_id`,`folder_id`,`title`);',1,'','2022-01-01 00:00:00'),
('create dashboard_provisioning','CREATE TABLE IF NOT EXISTS `dashboard_provisioning` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `dashboard_id` BIGINT(20) NULL\n, `name` VARCHAR(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `external_id` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Rename table dashboard_provisioning to dashboard_provisioning_tmp_qwerty - v1','ALTER TABLE `dashboard_provisioning` RENAME TO `dashboard_provisioning_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('create dashboard_provisioning v2','CREATE TABLE IF NOT EXISTS `dashboard_provisioning` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `dashboard_id` BIGINT(20) NULL\n, `name` VARCHAR(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `external_id` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `updated` INT NOT NULL DEFAULT 0\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index IDX_dashboard_provisioning_dashboard_id - v2','CREATE INDEX `IDX_dashboard_provisioning_dashboard_id` ON `dashboard_provisioning` (`dashboard_id`);',1,'','2022-01-01 00:00:00'),
('create index IDX_dashboard_provisioning_dashboard_id_name - v2','CREATE INDEX `IDX_dashboard_provisioning_dashboard_id_name` ON `dashboard_provisioning` (`dashboard_id`,`name`);',1,'','2022-01-01 00:00:00'),
('copy dashboard_provisioning v1 to v2','INSERT INTO `dashboard_provisioning` (`name`\n, `external_id`\n, `id`\n, `dashboard_id`)\nSELECT `name`\n, `external_id`\n, `id`\n, `dashboard_id`\nFROM `dashboard_provisioning_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('drop dashboard_provisioning_tmp_qwerty','DROP TABLE IF EXISTS `dashboard_provisioning_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('Add check_sum column','alter table `dashboard_provisioning` ADD COLUMN `check_sum` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add index for dashboard_title','CREATE INDEX `IDX_dashboard_title` ON `dashboard` (`title`);',1,'','2022-01-01 00:00:00'),
('delete tags for deleted dashboards','DELETE FROM dashboard_tag WHERE dashboard_id NOT IN (SELECT id FROM dashboard)',1,'','2022-01-01 00:00:00'),
('delete stars for deleted dashboards','DELETE FROM star WHERE dashboard_id NOT IN (SELECT id FROM dashboard)',1,'','2022-01-01 00:00:00'),
('Add index for dashboard_is_folder','CREATE INDEX `IDX_dashboard_is_folder` ON `dashboard` (`is_folder`);',1,'','2022-01-01 00:00:00'),
('Add isPublic for dashboard','alter table `dashboard` ADD COLUMN `is_public` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add deleted for dashboard','alter table `dashboard` ADD COLUMN `deleted` DATETIME NULL ',1,'','2022-01-01 00:00:00'),
('Add index for deleted','CREATE INDEX `IDX_dashboard_deleted` ON `dashboard` (`deleted`);',1,'','2022-01-01 00:00:00'),
('Add column dashboard_uid in dashboard_tag','alter table `dashboard_tag` ADD COLUMN `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add column org_id in dashboard_tag','alter table `dashboard_tag` ADD COLUMN `org_id` BIGINT(20) NULL DEFAULT 1 ',1,'','2022-01-01 00:00:00'),
('Add missing dashboard_uid and org_id to dashboard_tag','code migration',1,'','2022-01-01 00:00:00'),
('Add apiVersion for dashboard','alter table `dashboard` ADD COLUMN `api_version` VARCHAR(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add index for dashboard_uid on dashboard_tag table','CREATE INDEX `IDX_dashboard_tag_dashboard_uid` ON `dashboard_tag` (`dashboard_uid`);',1,'','2022-01-01 00:00:00'),
('Add missing dashboard_uid and org_id to star','code migration',1,'','2022-01-01 00:00:00'),
('create data_source table','CREATE TABLE IF NOT EXISTS `data_source` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `account_id` BIGINT(20) NOT NULL\n, `version` INT NOT NULL\n, `type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `access` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `user` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `database` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `basic_auth` TINYINT(1) NOT NULL\n, `basic_auth_user` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `basic_auth_password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `is_default` TINYINT(1) NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index data_source.account_id','CREATE INDEX `IDX_data_source_account_id` ON `data_source` (`account_id`);',1,'','2022-01-01 00:00:00'),
('add unique index data_source.account_id_name','CREATE UNIQUE INDEX `UQE_data_source_account_id_name` ON `data_source` (`account_id`,`name`);',1,'','2022-01-01 00:00:00'),
('drop index IDX_data_source_account_id - v1','DROP INDEX `IDX_data_source_account_id` ON `data_source`',1,'','2022-01-01 00:00:00'),
('drop index UQE_data_source_account_id_name - v1','DROP INDEX `UQE_data_source_account_id_name` ON `data_source`',1,'','2022-01-01 00:00:00'),
('Rename table data_source to data_source_v1 - v1','ALTER TABLE `data_source` RENAME TO `data_source_v1`',1,'','2022-01-01 00:00:00'),
('create data_source table v2','CREATE TABLE IF NOT EXISTS `data_source` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `version` INT NOT NULL\n, `type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `access` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `user` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `database` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `basic_auth` TINYINT(1) NOT NULL\n, `basic_auth_user` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `basic_auth_password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `is_default` TINYINT(1) NOT NULL\n, `json_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index IDX_data_source_org_id - v2','CREATE INDEX `IDX_data_source_org_id` ON `data_source` (`org_id`);',1,'','2022-01-01 00:00:00'),
('create index UQE_data_source_org_id_name - v2','CREATE UNIQUE INDEX `UQE_data_source_org_id_name` ON `data_source` (`org_id`,`name`);',1,'','2022-01-01 00:00:00'),
('Drop old table data_source_v1 #2','DROP TABLE IF EXISTS `data_source_v1`',1,'','2022-01-01 00:00:00'),
('Add column with_credentials','alter table `data_source` ADD COLUMN `with_credentials` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add secure json data column','alter table `data_source` ADD COLUMN `secure_json_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update data_source table charset','ALTER TABLE `data_source` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `access` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `user` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `database` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `basic_auth_user` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `basic_auth_password` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `json_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `secure_json_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ;',1,'','2022-01-01 00:00:00'),
('Update initial version to 1','UPDATE data_source SET version = 1 WHERE version = 0',1,'','2022-01-01 00:00:00'),
('Add read_only data column','alter table `data_source` ADD COLUMN `read_only` TINYINT(1) NULL ',1,'','2022-01-01 00:00:00'),
('Migrate logging ds to loki ds','UPDATE data_source SET type = \'loki\' WHERE type = \'logging\'',1,'','2022-01-01 00:00:00'),
('Update json_data with nulls','UPDATE data_source SET json_data = \'{}\' WHERE json_data is null',1,'','2022-01-01 00:00:00'),
('Add uid column','alter table `data_source` ADD COLUMN `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Update uid value','UPDATE data_source SET uid=lpad(id,9,\'0\');',1,'','2022-01-01 00:00:00'),
('Add unique index datasource_org_id_uid','CREATE UNIQUE INDEX `UQE_data_source_org_id_uid` ON `data_source` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('add unique index datasource_org_id_is_default','CREATE INDEX `IDX_data_source_org_id_is_default` ON `data_source` (`org_id`,`is_default`);',1,'','2022-01-01 00:00:00'),
('Add is_prunable column','alter table `data_source` ADD COLUMN `is_prunable` TINYINT(1) NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add api_version column','alter table `data_source` ADD COLUMN `api_version` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update secure_json_data column to MediumText','ALTER TABLE data_source MODIFY COLUMN secure_json_data MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('Update json_data column to MediumText','ALTER TABLE data_source MODIFY COLUMN json_data MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('create api_key table','CREATE TABLE IF NOT EXISTS `api_key` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `account_id` BIGINT(20) NOT NULL\n, `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `key` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `role` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index api_key.account_id','CREATE INDEX `IDX_api_key_account_id` ON `api_key` (`account_id`);',1,'','2022-01-01 00:00:00'),
('add index api_key.key','CREATE UNIQUE INDEX `UQE_api_key_key` ON `api_key` (`key`);',1,'','2022-01-01 00:00:00'),
('add index api_key.account_id_name','CREATE UNIQUE INDEX `UQE_api_key_account_id_name` ON `api_key` (`account_id`,`name`);',1,'','2022-01-01 00:00:00'),
('drop index IDX_api_key_account_id - v1','DROP INDEX `IDX_api_key_account_id` ON `api_key`',1,'','2022-01-01 00:00:00'),
('drop index UQE_api_key_key - v1','DROP INDEX `UQE_api_key_key` ON `api_key`',1,'','2022-01-01 00:00:00'),
('drop index UQE_api_key_account_id_name - v1','DROP INDEX `UQE_api_key_account_id_name` ON `api_key`',1,'','2022-01-01 00:00:00'),
('Rename table api_key to api_key_v1 - v1','ALTER TABLE `api_key` RENAME TO `api_key_v1`',1,'','2022-01-01 00:00:00'),
('create api_key table v2','CREATE TABLE IF NOT EXISTS `api_key` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `key` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `role` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index IDX_api_key_org_id - v2','CREATE INDEX `IDX_api_key_org_id` ON `api_key` (`org_id`);',1,'','2022-01-01 00:00:00'),
('create index UQE_api_key_key - v2','CREATE UNIQUE INDEX `UQE_api_key_key` ON `api_key` (`key`);',1,'','2022-01-01 00:00:00'),
('create index UQE_api_key_org_id_name - v2','CREATE UNIQUE INDEX `UQE_api_key_org_id_name` ON `api_key` (`org_id`,`name`);',1,'','2022-01-01 00:00:00'),
('copy api_key v1 to v2','INSERT INTO `api_key` (`name`\n, `key`\n, `role`\n, `created`\n, `updated`\n, `id`\n, `org_id`)\nSELECT `name`\n, `key`\n, `role`\n, `created`\n, `updated`\n, `id`\n, `account_id`\nFROM `api_key_v1`',1,'','2022-01-01 00:00:00'),
('Drop old table api_key_v1','DROP TABLE IF EXISTS `api_key_v1`',1,'','2022-01-01 00:00:00'),
('Update api_key table charset','ALTER TABLE `api_key` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `key` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `role` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Add expires to api_key table','alter table `api_key` ADD COLUMN `expires` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('Add service account foreign key','alter table `api_key` ADD COLUMN `service_account_id` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('set service account foreign key to nil if 0','UPDATE api_key SET service_account_id = NULL WHERE service_account_id = 0;',1,'','2022-01-01 00:00:00'),
('Add last_used_at to api_key table','alter table `api_key` ADD COLUMN `last_used_at` DATETIME NULL ',1,'','2022-01-01 00:00:00'),
('Add is_revoked column to api_key table','alter table `api_key` ADD COLUMN `is_revoked` TINYINT(1) NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('create dashboard_snapshot table v4','CREATE TABLE IF NOT EXISTS `dashboard_snapshot` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `key` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `dashboard` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `expires` DATETIME NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('drop table dashboard_snapshot_v4 #1','DROP TABLE IF EXISTS `dashboard_snapshot`',1,'','2022-01-01 00:00:00'),
('create dashboard_snapshot table v5 #2','CREATE TABLE IF NOT EXISTS `dashboard_snapshot` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `key` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `delete_key` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `external` TINYINT(1) NOT NULL\n, `external_url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `dashboard` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `expires` DATETIME NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_dashboard_snapshot_key - v5','CREATE UNIQUE INDEX `UQE_dashboard_snapshot_key` ON `dashboard_snapshot` (`key`);',1,'','2022-01-01 00:00:00'),
('create index UQE_dashboard_snapshot_delete_key - v5','CREATE UNIQUE INDEX `UQE_dashboard_snapshot_delete_key` ON `dashboard_snapshot` (`delete_key`);',1,'','2022-01-01 00:00:00'),
('create index IDX_dashboard_snapshot_user_id - v5','CREATE INDEX `IDX_dashboard_snapshot_user_id` ON `dashboard_snapshot` (`user_id`);',1,'','2022-01-01 00:00:00'),
('alter dashboard_snapshot to mediumtext v2','ALTER TABLE dashboard_snapshot MODIFY dashboard MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('Update dashboard_snapshot table charset','ALTER TABLE `dashboard_snapshot` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `key` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `delete_key` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `external_url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `dashboard` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Add column external_delete_url to dashboard_snapshots table','alter table `dashboard_snapshot` ADD COLUMN `external_delete_url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add encrypted dashboard json column','alter table `dashboard_snapshot` ADD COLUMN `dashboard_encrypted` BLOB NULL ',1,'','2022-01-01 00:00:00'),
('Change dashboard_encrypted column to MEDIUMBLOB','ALTER TABLE dashboard_snapshot MODIFY dashboard_encrypted MEDIUMBLOB;',1,'','2022-01-01 00:00:00'),
('create quota table v1','CREATE TABLE IF NOT EXISTS `quota` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NULL\n, `user_id` BIGINT(20) NULL\n, `target` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `limit` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_quota_org_id_user_id_target - v1','CREATE UNIQUE INDEX `UQE_quota_org_id_user_id_target` ON `quota` (`org_id`,`user_id`,`target`);',1,'','2022-01-01 00:00:00'),
('Update quota table charset','ALTER TABLE `quota` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `target` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('create plugin_setting table','CREATE TABLE IF NOT EXISTS `plugin_setting` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NULL\n, `plugin_id` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `enabled` TINYINT(1) NOT NULL\n, `pinned` TINYINT(1) NOT NULL\n, `json_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `secure_json_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_plugin_setting_org_id_plugin_id - v1','CREATE UNIQUE INDEX `UQE_plugin_setting_org_id_plugin_id` ON `plugin_setting` (`org_id`,`plugin_id`);',1,'','2022-01-01 00:00:00'),
('Add column plugin_version to plugin_settings','alter table `plugin_setting` ADD COLUMN `plugin_version` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update plugin_setting table charset','ALTER TABLE `plugin_setting` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `plugin_id` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `json_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `secure_json_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `plugin_version` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ;',1,'','2022-01-01 00:00:00'),
('update NULL org_id to 1','UPDATE plugin_setting SET org_id=1 where org_id IS NULL;',1,'','2022-01-01 00:00:00'),
('make org_id NOT NULL and DEFAULT VALUE 1','ALTER TABLE plugin_setting MODIFY COLUMN org_id BIGINT NOT NULL DEFAULT 1;',1,'','2022-01-01 00:00:00'),
('create session table','CREATE TABLE IF NOT EXISTS `session` (\n`key` CHAR(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `data` BLOB NOT NULL\n, `expiry` INTEGER(255) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Drop old table playlist table','DROP TABLE IF EXISTS `playlist`',1,'','2022-01-01 00:00:00'),
('Drop old table playlist_item table','DROP TABLE IF EXISTS `playlist_item`',1,'','2022-01-01 00:00:00'),
('create playlist table v2','CREATE TABLE IF NOT EXISTS `playlist` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `interval` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create playlist item table v2','CREATE TABLE IF NOT EXISTS `playlist_item` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `playlist_id` BIGINT(20) NOT NULL\n, `type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `value` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `title` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `order` INT NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Update playlist table charset','ALTER TABLE `playlist` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `interval` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Update playlist_item table charset','ALTER TABLE `playlist_item` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `value` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `title` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Add playlist column created_at','alter table `playlist` ADD COLUMN `created_at` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add playlist column updated_at','alter table `playlist` ADD COLUMN `updated_at` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('drop preferences table v2','DROP TABLE IF EXISTS `preferences`',1,'','2022-01-01 00:00:00'),
('drop preferences table v3','DROP TABLE IF EXISTS `preferences`',1,'','2022-01-01 00:00:00'),
('create preferences table v3','CREATE TABLE IF NOT EXISTS `preferences` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `version` INT NOT NULL\n, `home_dashboard_id` BIGINT(20) NOT NULL\n, `timezone` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `theme` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Update preferences table charset','ALTER TABLE `preferences` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `timezone` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `theme` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Add column team_id in preferences','alter table `preferences` ADD COLUMN `team_id` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('Update team_id column values in preferences','UPDATE preferences SET team_id=0 WHERE team_id IS NULL;',1,'','2022-01-01 00:00:00'),
('Add column week_start in preferences','alter table `preferences` ADD COLUMN `week_start` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add column preferences.json_data','alter table `preferences` ADD COLUMN `json_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('alter preferences.json_data to mediumtext v1','ALTER TABLE preferences MODIFY json_data MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('Add preferences index org_id','CREATE INDEX `IDX_preferences_org_id` ON `preferences` (`org_id`);',1,'','2022-01-01 00:00:00'),
('Add preferences index user_id','CREATE INDEX `IDX_preferences_user_id` ON `preferences` (`user_id`);',1,'','2022-01-01 00:00:00'),
('Add home_dashboard_uid column to preferences table','alter table `preferences` ADD COLUMN `home_dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add missing dashboard_uid to preferences table','code migration',1,'','2022-01-01 00:00:00'),
('create alert table v1','CREATE TABLE IF NOT EXISTS `alert` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `version` BIGINT(20) NOT NULL\n, `dashboard_id` BIGINT(20) NOT NULL\n, `panel_id` BIGINT(20) NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `message` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `state` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `frequency` BIGINT(20) NOT NULL\n, `handler` BIGINT(20) NOT NULL\n, `severity` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `silenced` TINYINT(1) NOT NULL\n, `execution_error` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `eval_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `eval_date` DATETIME NULL\n, `new_state_date` DATETIME NOT NULL\n, `state_changes` INT NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index alert org_id & id ','CREATE INDEX `IDX_alert_org_id_id` ON `alert` (`org_id`,`id`);',1,'','2022-01-01 00:00:00'),
('add index alert state','CREATE INDEX `IDX_alert_state` ON `alert` (`state`);',1,'','2022-01-01 00:00:00'),
('add index alert dashboard_id','CREATE INDEX `IDX_alert_dashboard_id` ON `alert` (`dashboard_id`);',1,'','2022-01-01 00:00:00'),
('Create alert_rule_tag table v1','CREATE TABLE IF NOT EXISTS `alert_rule_tag` (\n`alert_id` BIGINT(20) NOT NULL\n, `tag_id` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add unique index alert_rule_tag.alert_id_tag_id','CREATE UNIQUE INDEX `UQE_alert_rule_tag_alert_id_tag_id` ON `alert_rule_tag` (`alert_id`,`tag_id`);',1,'','2022-01-01 00:00:00'),
('drop index UQE_alert_rule_tag_alert_id_tag_id - v1','DROP INDEX `UQE_alert_rule_tag_alert_id_tag_id` ON `alert_rule_tag`',1,'','2022-01-01 00:00:00'),
('Rename table alert_rule_tag to alert_rule_tag_v1 - v1','ALTER TABLE `alert_rule_tag` RENAME TO `alert_rule_tag_v1`',1,'','2022-01-01 00:00:00'),
('Create alert_rule_tag table v2','CREATE TABLE IF NOT EXISTS `alert_rule_tag` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `alert_id` BIGINT(20) NOT NULL\n, `tag_id` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_alert_rule_tag_alert_id_tag_id - Add unique index alert_rule_tag.alert_id_tag_id V2','CREATE UNIQUE INDEX `UQE_alert_rule_tag_alert_id_tag_id` ON `alert_rule_tag` (`alert_id`,`tag_id`);',1,'','2022-01-01 00:00:00'),
('copy alert_rule_tag v1 to v2','INSERT INTO `alert_rule_tag` (`alert_id`\n, `tag_id`)\nSELECT `alert_id`\n, `tag_id`\nFROM `alert_rule_tag_v1`',1,'','2022-01-01 00:00:00'),
('drop table alert_rule_tag_v1','DROP TABLE IF EXISTS `alert_rule_tag_v1`',1,'','2022-01-01 00:00:00'),
('create alert_notification table v1','CREATE TABLE IF NOT EXISTS `alert_notification` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add column is_default','alter table `alert_notification` ADD COLUMN `is_default` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add column frequency','alter table `alert_notification` ADD COLUMN `frequency` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('Add column send_reminder','alter table `alert_notification` ADD COLUMN `send_reminder` TINYINT(1) NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add column disable_resolve_message','alter table `alert_notification` ADD COLUMN `disable_resolve_message` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add index alert_notification org_id & name','CREATE UNIQUE INDEX `UQE_alert_notification_org_id_name` ON `alert_notification` (`org_id`,`name`);',1,'','2022-01-01 00:00:00'),
('Update alert table charset','ALTER TABLE `alert` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `message` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `state` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `severity` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `execution_error` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `eval_data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ;',1,'','2022-01-01 00:00:00'),
('Update alert_notification table charset','ALTER TABLE `alert_notification` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('create notification_journal table v1','CREATE TABLE IF NOT EXISTS `alert_notification_journal` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `alert_id` BIGINT(20) NOT NULL\n, `notifier_id` BIGINT(20) NOT NULL\n, `sent_at` BIGINT(20) NOT NULL\n, `success` TINYINT(1) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index notification_journal org_id & alert_id & notifier_id','CREATE INDEX `IDX_alert_notification_journal_org_id_alert_id_notifier_id` ON `alert_notification_journal` (`org_id`,`alert_id`,`notifier_id`);',1,'','2022-01-01 00:00:00'),
('drop alert_notification_journal','DROP TABLE IF EXISTS `alert_notification_journal`',1,'','2022-01-01 00:00:00'),
('create alert_notification_state table v1','CREATE TABLE IF NOT EXISTS `alert_notification_state` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `alert_id` BIGINT(20) NOT NULL\n, `notifier_id` BIGINT(20) NOT NULL\n, `state` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `version` BIGINT(20) NOT NULL\n, `updated_at` BIGINT(20) NOT NULL\n, `alert_rule_state_updated_version` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index alert_notification_state org_id & alert_id & notifier_id','CREATE UNIQUE INDEX `UQE_alert_notification_state_org_id_alert_id_notifier_id` ON `alert_notification_state` (`org_id`,`alert_id`,`notifier_id`);',1,'','2022-01-01 00:00:00'),
('Add for to alert table','alter table `alert` ADD COLUMN `for` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('Add column uid in alert_notification','alter table `alert_notification` ADD COLUMN `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update uid column values in alert_notification','UPDATE alert_notification SET uid=lpad(id,9,\'0\') WHERE uid IS NULL;',1,'','2022-01-01 00:00:00'),
('Add unique index alert_notification_org_id_uid','CREATE UNIQUE INDEX `UQE_alert_notification_org_id_uid` ON `alert_notification` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('Remove unique index org_id_name','DROP INDEX `UQE_alert_notification_org_id_name` ON `alert_notification`',1,'','2022-01-01 00:00:00'),
('Add column secure_settings in alert_notification','alter table `alert_notification` ADD COLUMN `secure_settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('alter alert.settings to mediumtext','ALTER TABLE alert MODIFY settings MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('Add non-unique index alert_notification_state_alert_id','CREATE INDEX `IDX_alert_notification_state_alert_id` ON `alert_notification_state` (`alert_id`);',1,'','2022-01-01 00:00:00'),
('Add non-unique index alert_rule_tag_alert_id','CREATE INDEX `IDX_alert_rule_tag_alert_id` ON `alert_rule_tag` (`alert_id`);',1,'','2022-01-01 00:00:00'),
('Drop old annotation table v4','DROP TABLE IF EXISTS `annotation`',1,'','2022-01-01 00:00:00'),
('create annotation table v5','CREATE TABLE IF NOT EXISTS `annotation` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `alert_id` BIGINT(20) NULL\n, `user_id` BIGINT(20) NULL\n, `dashboard_id` BIGINT(20) NULL\n, `panel_id` BIGINT(20) NULL\n, `category_id` BIGINT(20) NULL\n, `type` VARCHAR(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `title` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `text` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `metric` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `prev_state` VARCHAR(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `new_state` VARCHAR(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `epoch` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index annotation 0 v3','CREATE INDEX `IDX_annotation_org_id_alert_id` ON `annotation` (`org_id`,`alert_id`);',1,'','2022-01-01 00:00:00'),
('add index annotation 1 v3','CREATE INDEX `IDX_annotation_org_id_type` ON `annotation` (`org_id`,`type`);',1,'','2022-01-01 00:00:00'),
('add index annotation 2 v3','CREATE INDEX `IDX_annotation_org_id_category_id` ON `annotation` (`org_id`,`category_id`);',1,'','2022-01-01 00:00:00'),
('add index annotation 3 v3','CREATE INDEX `IDX_annotation_org_id_dashboard_id_panel_id_epoch` ON `annotation` (`org_id`,`dashboard_id`,`panel_id`,`epoch`);',1,'','2022-01-01 00:00:00'),
('add index annotation 4 v3','CREATE INDEX `IDX_annotation_org_id_epoch` ON `annotation` (`org_id`,`epoch`);',1,'','2022-01-01 00:00:00'),
('Update annotation table charset','ALTER TABLE `annotation` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `type` VARCHAR(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `title` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `text` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `metric` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL , MODIFY `prev_state` VARCHAR(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `new_state` VARCHAR(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL , MODIFY `data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Add column region_id to annotation table','alter table `annotation` ADD COLUMN `region_id` BIGINT(20) NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Drop category_id index','DROP INDEX `IDX_annotation_org_id_category_id` ON `annotation`',1,'','2022-01-01 00:00:00'),
('Add column tags to annotation table','alter table `annotation` ADD COLUMN `tags` VARCHAR(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Create annotation_tag table v2','CREATE TABLE IF NOT EXISTS `annotation_tag` (\n`annotation_id` BIGINT(20) NOT NULL\n, `tag_id` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add unique index annotation_tag.annotation_id_tag_id','CREATE UNIQUE INDEX `UQE_annotation_tag_annotation_id_tag_id` ON `annotation_tag` (`annotation_id`,`tag_id`);',1,'','2022-01-01 00:00:00'),
('drop index UQE_annotation_tag_annotation_id_tag_id - v2','DROP INDEX `UQE_annotation_tag_annotation_id_tag_id` ON `annotation_tag`',1,'','2022-01-01 00:00:00'),
('Rename table annotation_tag to annotation_tag_v2 - v2','ALTER TABLE `annotation_tag` RENAME TO `annotation_tag_v2`',1,'','2022-01-01 00:00:00'),
('Create annotation_tag table v3','CREATE TABLE IF NOT EXISTS `annotation_tag` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `annotation_id` BIGINT(20) NOT NULL\n, `tag_id` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_annotation_tag_annotation_id_tag_id - Add unique index annotation_tag.annotation_id_tag_id V3','CREATE UNIQUE INDEX `UQE_annotation_tag_annotation_id_tag_id` ON `annotation_tag` (`annotation_id`,`tag_id`);',1,'','2022-01-01 00:00:00'),
('copy annotation_tag v2 to v3','INSERT INTO `annotation_tag` (`annotation_id`\n, `tag_id`)\nSELECT `annotation_id`\n, `tag_id`\nFROM `annotation_tag_v2`',1,'','2022-01-01 00:00:00'),
('drop table annotation_tag_v2','DROP TABLE IF EXISTS `annotation_tag_v2`',1,'','2022-01-01 00:00:00'),
('Update alert annotations and set TEXT to empty','UPDATE annotation SET TEXT = \'\' WHERE alert_id > 0',1,'','2022-01-01 00:00:00'),
('Add created time to annotation table','alter table `annotation` ADD COLUMN `created` BIGINT(20) NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add updated time to annotation table','alter table `annotation` ADD COLUMN `updated` BIGINT(20) NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add index for created in annotation table','CREATE INDEX `IDX_annotation_org_id_created` ON `annotation` (`org_id`,`created`);',1,'','2022-01-01 00:00:00'),
('Add index for updated in annotation table','CREATE INDEX `IDX_annotation_org_id_updated` ON `annotation` (`org_id`,`updated`);',1,'','2022-01-01 00:00:00'),
('Convert existing annotations from seconds to milliseconds','UPDATE annotation SET epoch = (epoch*1000) where epoch < 9999999999',1,'','2022-01-01 00:00:00'),
('Add epoch_end column','alter table `annotation` ADD COLUMN `epoch_end` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add index for epoch_end','CREATE INDEX `IDX_annotation_org_id_epoch_epoch_end` ON `annotation` (`org_id`,`epoch`,`epoch_end`);',1,'','2022-01-01 00:00:00'),
('Make epoch_end the same as epoch','UPDATE annotation SET epoch_end = epoch',1,'','2022-01-01 00:00:00'),
('Move region to single row','code migration',1,'','2022-01-01 00:00:00'),
('Remove index org_id_epoch from annotation table','DROP INDEX `IDX_annotation_org_id_epoch` ON `annotation`',1,'','2022-01-01 00:00:00'),
('Remove index org_id_dashboard_id_panel_id_epoch from annotation table','DROP INDEX `IDX_annotation_org_id_dashboard_id_panel_id_epoch` ON `annotation`',1,'','2022-01-01 00:00:00'),
('Add index for org_id_dashboard_id_epoch_end_epoch on annotation table','CREATE INDEX `IDX_annotation_org_id_dashboard_id_epoch_end_epoch` ON `annotation` (`org_id`,`dashboard_id`,`epoch_end`,`epoch`);',1,'','2022-01-01 00:00:00'),
('Add index for org_id_epoch_end_epoch on annotation table','CREATE INDEX `IDX_annotation_org_id_epoch_end_epoch` ON `annotation` (`org_id`,`epoch_end`,`epoch`);',1,'','2022-01-01 00:00:00'),
('Remove index org_id_epoch_epoch_end from annotation table','DROP INDEX `IDX_annotation_org_id_epoch_epoch_end` ON `annotation`',1,'','2022-01-01 00:00:00'),
('Add index for alert_id on annotation table','CREATE INDEX `IDX_annotation_alert_id` ON `annotation` (`alert_id`);',1,'','2022-01-01 00:00:00'),
('Increase tags column to length 4096','ALTER TABLE annotation MODIFY tags VARCHAR(4096);',1,'','2022-01-01 00:00:00'),
('Increase prev_state column to length 40 not null','ALTER TABLE annotation MODIFY prev_state VARCHAR(40) NOT NULL;',1,'','2022-01-01 00:00:00'),
('Increase new_state column to length 40 not null','ALTER TABLE annotation MODIFY new_state VARCHAR(40) NOT NULL;',1,'','2022-01-01 00:00:00'),
('Add dashboard_uid column to annotation table','alter table `annotation` ADD COLUMN `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add missing dashboard_uid to annotation table','code migration',1,'','2022-01-01 00:00:00'),
('create test_data table','CREATE TABLE IF NOT EXISTS `test_data` (\n`id` INT PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `metric1` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `metric2` VARCHAR(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `value_big_int` BIGINT(20) NULL\n, `value_double` DOUBLE NULL\n, `value_float` FLOAT NULL\n, `value_int` INT NULL\n, `time_epoch` BIGINT(20) NOT NULL\n, `time_date_time` DATETIME NOT NULL\n, `time_time_stamp` TIMESTAMP NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create dashboard_version table v1','CREATE TABLE IF NOT EXISTS `dashboard_version` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `dashboard_id` BIGINT(20) NOT NULL\n, `parent_version` INT NOT NULL\n, `restored_from` INT NOT NULL\n, `version` INT NOT NULL\n, `created` DATETIME NOT NULL\n, `created_by` BIGINT(20) NOT NULL\n, `message` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index dashboard_version.dashboard_id','CREATE INDEX `IDX_dashboard_version_dashboard_id` ON `dashboard_version` (`dashboard_id`);',1,'','2022-01-01 00:00:00'),
('add unique index dashboard_version.dashboard_id and dashboard_version.version','CREATE UNIQUE INDEX `UQE_dashboard_version_dashboard_id_version` ON `dashboard_version` (`dashboard_id`,`version`);',1,'','2022-01-01 00:00:00'),
('Set dashboard version to 1 where 0','UPDATE dashboard SET version = 1 WHERE version = 0',1,'','2022-01-01 00:00:00'),
('save existing dashboard data in dashboard_version table v1','INSERT INTO dashboard_version\n(\n dashboard_id,\n version,\n parent_version,\n restored_from,\n created,\n created_by,\n message,\n data\n)\nSELECT\n dashboard.id,\n dashboard.version,\n dashboard.version,\n dashboard.version,\n dashboard.updated,\n COALESCE(dashboard.updated_by, -1),\n \'\',\n dashboard.data\nFROM dashboard;',1,'','2022-01-01 00:00:00'),
('alter dashboard_version.data to mediumtext v1','ALTER TABLE dashboard_version MODIFY data MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('Add apiVersion for dashboard_version','alter table `dashboard_version` ADD COLUMN `api_version` VARCHAR(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('create team table','CREATE TABLE IF NOT EXISTS `team` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index team.org_id','CREATE INDEX `IDX_team_org_id` ON `team` (`org_id`);',1,'','2022-01-01 00:00:00'),
('add unique index team_org_id_name','CREATE UNIQUE INDEX `UQE_team_org_id_name` ON `team` (`org_id`,`name`);',1,'','2022-01-01 00:00:00'),
('Add column uid in team','alter table `team` ADD COLUMN `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update uid column values in team','UPDATE team SET uid=concat(\'t\',lpad(id,9,\'0\')) WHERE uid IS NULL;',1,'','2022-01-01 00:00:00'),
('Add unique index team_org_id_uid','CREATE UNIQUE INDEX `UQE_team_org_id_uid` ON `team` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('Add column external_uid in team','alter table `team` ADD COLUMN `external_uid` VARCHAR(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add column is_provisioned in team','alter table `team` ADD COLUMN `is_provisioned` TINYINT(1) NULL ',1,'','2022-01-01 00:00:00'),
('create team member table','CREATE TABLE IF NOT EXISTS `team_member` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `team_id` BIGINT(20) NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index team_member.org_id','CREATE INDEX `IDX_team_member_org_id` ON `team_member` (`org_id`);',1,'','2022-01-01 00:00:00'),
('add unique index team_member_org_id_team_id_user_id','CREATE UNIQUE INDEX `UQE_team_member_org_id_team_id_user_id` ON `team_member` (`org_id`,`team_id`,`user_id`);',1,'','2022-01-01 00:00:00'),
('add index team_member.team_id','CREATE INDEX `IDX_team_member_team_id` ON `team_member` (`team_id`);',1,'','2022-01-01 00:00:00'),
('Add column email to team table','alter table `team` ADD COLUMN `email` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add column external to team_member table','alter table `team_member` ADD COLUMN `external` TINYINT(1) NULL ',1,'','2022-01-01 00:00:00'),
('Add column permission to team_member table','alter table `team_member` ADD COLUMN `permission` SMALLINT NULL ',1,'','2022-01-01 00:00:00'),
('add unique index team_member_user_id_org_id','CREATE INDEX `IDX_team_member_user_id_org_id` ON `team_member` (`user_id`,`org_id`);',1,'','2022-01-01 00:00:00'),
('Add column uid in team_member','alter table `team_member` ADD COLUMN `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update uid column values in team_member','UPDATE team_member SET uid=concat(\'tm\',lpad(id,9,\'0\')) WHERE uid IS NULL OR uid = \'\';',1,'','2022-01-01 00:00:00'),
('Add unique index team_member_uid','CREATE UNIQUE INDEX `UQE_team_member_uid` ON `team_member` (`uid`);',1,'','2022-01-01 00:00:00'),
('create dashboard acl table','CREATE TABLE IF NOT EXISTS `dashboard_acl` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `dashboard_id` BIGINT(20) NOT NULL\n, `user_id` BIGINT(20) NULL\n, `team_id` BIGINT(20) NULL\n, `permission` SMALLINT NOT NULL DEFAULT 4\n, `role` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index dashboard_acl_dashboard_id','CREATE INDEX `IDX_dashboard_acl_dashboard_id` ON `dashboard_acl` (`dashboard_id`);',1,'','2022-01-01 00:00:00'),
('add unique index dashboard_acl_dashboard_id_user_id','CREATE UNIQUE INDEX `UQE_dashboard_acl_dashboard_id_user_id` ON `dashboard_acl` (`dashboard_id`,`user_id`);',1,'','2022-01-01 00:00:00'),
('add unique index dashboard_acl_dashboard_id_team_id','CREATE UNIQUE INDEX `UQE_dashboard_acl_dashboard_id_team_id` ON `dashboard_acl` (`dashboard_id`,`team_id`);',1,'','2022-01-01 00:00:00'),
('add index dashboard_acl_user_id','CREATE INDEX `IDX_dashboard_acl_user_id` ON `dashboard_acl` (`user_id`);',1,'','2022-01-01 00:00:00'),
('add index dashboard_acl_team_id','CREATE INDEX `IDX_dashboard_acl_team_id` ON `dashboard_acl` (`team_id`);',1,'','2022-01-01 00:00:00'),
('add index dashboard_acl_org_id_role','CREATE INDEX `IDX_dashboard_acl_org_id_role` ON `dashboard_acl` (`org_id`,`role`);',1,'','2022-01-01 00:00:00'),
('add index dashboard_permission','CREATE INDEX `IDX_dashboard_acl_permission` ON `dashboard_acl` (`permission`);',1,'','2022-01-01 00:00:00'),
('save default acl rules in dashboard_acl table','\nINSERT INTO dashboard_acl\n (\n org_id,\n dashboard_id,\n permission,\n role,\n created,\n updated\n )\n VALUES\n (-1,-1, 1,\'Viewer\',\'2017-06-20\',\'2017-06-20\'),\n (-1,-1, 2,\'Editor\',\'2017-06-20\',\'2017-06-20\')\n ',1,'','2022-01-01 00:00:00'),
('delete acl rules for deleted dashboards and folders','DELETE FROM dashboard_acl WHERE dashboard_id NOT IN (SELECT id FROM dashboard) AND dashboard_id != -1',1,'','2022-01-01 00:00:00'),
('create tag table','CREATE TABLE IF NOT EXISTS `tag` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `key` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `value` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index tag.key_value','CREATE UNIQUE INDEX `UQE_tag_key_value` ON `tag` (`key`,`value`);',1,'','2022-01-01 00:00:00'),
('create login attempt table','CREATE TABLE IF NOT EXISTS `login_attempt` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `username` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `ip_address` VARCHAR(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index login_attempt.username','CREATE INDEX `IDX_login_attempt_username` ON `login_attempt` (`username`);',1,'','2022-01-01 00:00:00'),
('drop index IDX_login_attempt_username - v1','DROP INDEX `IDX_login_attempt_username` ON `login_attempt`',1,'','2022-01-01 00:00:00'),
('Rename table login_attempt to login_attempt_tmp_qwerty - v1','ALTER TABLE `login_attempt` RENAME TO `login_attempt_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('create login_attempt v2','CREATE TABLE IF NOT EXISTS `login_attempt` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `username` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `ip_address` VARCHAR(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` INT NOT NULL DEFAULT 0\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index IDX_login_attempt_username - v2','CREATE INDEX `IDX_login_attempt_username` ON `login_attempt` (`username`);',1,'','2022-01-01 00:00:00'),
('copy login_attempt v1 to v2','INSERT INTO `login_attempt` (`id`\n, `username`\n, `ip_address`)\nSELECT `id`\n, `username`\n, `ip_address`\nFROM `login_attempt_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('drop login_attempt_tmp_qwerty','DROP TABLE IF EXISTS `login_attempt_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('increase login_attempt.ip_address column length for IPv6 support','ALTER TABLE login_attempt MODIFY ip_address VARCHAR(50);',1,'','2022-01-01 00:00:00'),
('alter table login_attempt alter column created type to bigint','ALTER TABLE login_attempt MODIFY created BIGINT;',1,'','2022-01-01 00:00:00'),
('create user auth table','CREATE TABLE IF NOT EXISTS `user_auth` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `auth_module` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `auth_id` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index IDX_user_auth_auth_module_auth_id - v1','CREATE INDEX `IDX_user_auth_auth_module_auth_id` ON `user_auth` (`auth_module`,`auth_id`);',1,'','2022-01-01 00:00:00'),
('alter user_auth.auth_id to length 190','ALTER TABLE user_auth MODIFY auth_id VARCHAR(190);',1,'','2022-01-01 00:00:00'),
('Add OAuth access token to user_auth','alter table `user_auth` ADD COLUMN `o_auth_access_token` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add OAuth refresh token to user_auth','alter table `user_auth` ADD COLUMN `o_auth_refresh_token` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add OAuth token type to user_auth','alter table `user_auth` ADD COLUMN `o_auth_token_type` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add OAuth expiry to user_auth','alter table `user_auth` ADD COLUMN `o_auth_expiry` DATETIME NULL ',1,'','2022-01-01 00:00:00'),
('Add index to user_id column in user_auth','CREATE INDEX `IDX_user_auth_user_id` ON `user_auth` (`user_id`);',1,'','2022-01-01 00:00:00'),
('Add OAuth ID token to user_auth','alter table `user_auth` ADD COLUMN `o_auth_id_token` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add user_unique_id to user_auth','alter table `user_auth` ADD COLUMN `external_uid` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('create server_lock table','CREATE TABLE IF NOT EXISTS `server_lock` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `operation_uid` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `version` BIGINT(20) NOT NULL\n, `last_execution` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index server_lock.operation_uid','CREATE UNIQUE INDEX `UQE_server_lock_operation_uid` ON `server_lock` (`operation_uid`);',1,'','2022-01-01 00:00:00'),
('create user auth token table','CREATE TABLE IF NOT EXISTS `user_auth_token` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `auth_token` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `prev_auth_token` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `user_agent` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `client_ip` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `auth_token_seen` TINYINT(1) NOT NULL\n, `seen_at` INT NULL\n, `rotated_at` INT NOT NULL\n, `created_at` INT NOT NULL\n, `updated_at` INT NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index user_auth_token.auth_token','CREATE UNIQUE INDEX `UQE_user_auth_token_auth_token` ON `user_auth_token` (`auth_token`);',1,'','2022-01-01 00:00:00'),
('add unique index user_auth_token.prev_auth_token','CREATE UNIQUE INDEX `UQE_user_auth_token_prev_auth_token` ON `user_auth_token` (`prev_auth_token`);',1,'','2022-01-01 00:00:00'),
('add index user_auth_token.user_id','CREATE INDEX `IDX_user_auth_token_user_id` ON `user_auth_token` (`user_id`);',1,'','2022-01-01 00:00:00'),
('Add revoked_at to the user auth token','alter table `user_auth_token` ADD COLUMN `revoked_at` INT NULL ',1,'','2022-01-01 00:00:00'),
('add index user_auth_token.revoked_at','CREATE INDEX `IDX_user_auth_token_revoked_at` ON `user_auth_token` (`revoked_at`);',1,'','2022-01-01 00:00:00'),
('add external_session_id to user_auth_token','alter table `user_auth_token` ADD COLUMN `external_session_id` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('create cache_data table','CREATE TABLE IF NOT EXISTS `cache_data` (\n`cache_key` VARCHAR(168) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `data` BLOB NOT NULL\n, `expires` INTEGER(255) NOT NULL\n, `created_at` INTEGER(255) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index cache_data.cache_key','CREATE UNIQUE INDEX `UQE_cache_data_cache_key` ON `cache_data` (`cache_key`);',1,'','2022-01-01 00:00:00'),
('create short_url table v1','CREATE TABLE IF NOT EXISTS `short_url` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `path` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created_by` INT NOT NULL\n, `created_at` INT NOT NULL\n, `last_seen_at` INT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index short_url.org_id-uid','CREATE UNIQUE INDEX `UQE_short_url_org_id_uid` ON `short_url` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('alter table short_url alter column created_by type to bigint','ALTER TABLE short_url MODIFY created_by BIGINT;',1,'','2022-01-01 00:00:00'),
('delete alert_definition table','DROP TABLE IF EXISTS `alert_definition`',1,'','2022-01-01 00:00:00'),
('recreate alert_definition table','CREATE TABLE IF NOT EXISTS `alert_definition` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `title` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `condition` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `updated` DATETIME NOT NULL\n, `interval_seconds` BIGINT(20) NOT NULL DEFAULT 60\n, `version` INT NOT NULL DEFAULT 0\n, `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 0\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index in alert_definition on org_id and title columns','CREATE INDEX `IDX_alert_definition_org_id_title` ON `alert_definition` (`org_id`,`title`);',1,'','2022-01-01 00:00:00'),
('add index in alert_definition on org_id and uid columns','CREATE INDEX `IDX_alert_definition_org_id_uid` ON `alert_definition` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('alter alert_definition table data column to mediumtext in mysql','ALTER TABLE alert_definition MODIFY data MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('drop index in alert_definition on org_id and title columns','DROP INDEX `IDX_alert_definition_org_id_title` ON `alert_definition`',1,'','2022-01-01 00:00:00'),
('drop index in alert_definition on org_id and uid columns','DROP INDEX `IDX_alert_definition_org_id_uid` ON `alert_definition`',1,'','2022-01-01 00:00:00'),
('add unique index in alert_definition on org_id and title columns','CREATE UNIQUE INDEX `UQE_alert_definition_org_id_title` ON `alert_definition` (`org_id`,`title`);',1,'','2022-01-01 00:00:00'),
('add unique index in alert_definition on org_id and uid columns','CREATE UNIQUE INDEX `UQE_alert_definition_org_id_uid` ON `alert_definition` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('Add column paused in alert_definition','alter table `alert_definition` ADD COLUMN `paused` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('drop alert_definition table','DROP TABLE IF EXISTS `alert_definition`',1,'','2022-01-01 00:00:00'),
('delete alert_definition_version table','DROP TABLE IF EXISTS `alert_definition_version`',1,'','2022-01-01 00:00:00'),
('recreate alert_definition_version table','CREATE TABLE IF NOT EXISTS `alert_definition_version` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `alert_definition_id` BIGINT(20) NOT NULL\n, `alert_definition_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 0\n, `parent_version` INT NOT NULL\n, `restored_from` INT NOT NULL\n, `version` INT NOT NULL\n, `created` DATETIME NOT NULL\n, `title` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `condition` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `interval_seconds` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index in alert_definition_version table on alert_definition_id and version columns','CREATE UNIQUE INDEX `UQE_alert_definition_version_alert_definition_id_version` ON `alert_definition_version` (`alert_definition_id`,`version`);',1,'','2022-01-01 00:00:00'),
('add index in alert_definition_version table on alert_definition_uid and version columns','CREATE UNIQUE INDEX `UQE_alert_definition_version_alert_definition_uid_version` ON `alert_definition_version` (`alert_definition_uid`,`version`);',1,'','2022-01-01 00:00:00'),
('alter alert_definition_version table data column to mediumtext in mysql','ALTER TABLE alert_definition_version MODIFY data MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('drop alert_definition_version table','DROP TABLE IF EXISTS `alert_definition_version`',1,'','2022-01-01 00:00:00'),
('create alert_instance table','CREATE TABLE IF NOT EXISTS `alert_instance` (\n`def_org_id` BIGINT(20) NOT NULL\n, `def_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 0\n, `labels` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `labels_hash` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `current_state` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `current_state_since` BIGINT(20) NOT NULL\n, `last_eval_time` BIGINT(20) NOT NULL\n, PRIMARY KEY ( `def_org_id`,`def_uid`,`labels_hash` )) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index in alert_instance table on def_org_id, def_uid and current_state columns','CREATE INDEX `IDX_alert_instance_def_org_id_def_uid_current_state` ON `alert_instance` (`def_org_id`,`def_uid`,`current_state`);',1,'','2022-01-01 00:00:00'),
('add index in alert_instance table on def_org_id, current_state columns','CREATE INDEX `IDX_alert_instance_def_org_id_current_state` ON `alert_instance` (`def_org_id`,`current_state`);',1,'','2022-01-01 00:00:00'),
('add column current_state_end to alert_instance','alter table `alert_instance` ADD COLUMN `current_state_end` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('remove index def_org_id, def_uid, current_state on alert_instance','DROP INDEX `IDX_alert_instance_def_org_id_def_uid_current_state` ON `alert_instance`',1,'','2022-01-01 00:00:00'),
('remove index def_org_id, current_state on alert_instance','DROP INDEX `IDX_alert_instance_def_org_id_current_state` ON `alert_instance`',1,'','2022-01-01 00:00:00'),
('rename def_org_id to rule_org_id in alert_instance','ALTER TABLE alert_instance CHANGE def_org_id rule_org_id BIGINT;',1,'','2022-01-01 00:00:00'),
('rename def_uid to rule_uid in alert_instance','ALTER TABLE alert_instance CHANGE def_uid rule_uid VARCHAR(40);',1,'','2022-01-01 00:00:00'),
('add index rule_org_id, rule_uid, current_state on alert_instance','CREATE INDEX `IDX_alert_instance_rule_org_id_rule_uid_current_state` ON `alert_instance` (`rule_org_id`,`rule_uid`,`current_state`);',1,'','2022-01-01 00:00:00'),
('add index rule_org_id, current_state on alert_instance','CREATE INDEX `IDX_alert_instance_rule_org_id_current_state` ON `alert_instance` (`rule_org_id`,`current_state`);',1,'','2022-01-01 00:00:00'),
('add current_reason column related to current_state','alter table `alert_instance` ADD COLUMN `current_reason` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add result_fingerprint column to alert_instance','alter table `alert_instance` ADD COLUMN `result_fingerprint` VARCHAR(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('create alert_rule table','CREATE TABLE IF NOT EXISTS `alert_rule` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `title` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `condition` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `updated` DATETIME NOT NULL\n, `interval_seconds` BIGINT(20) NOT NULL DEFAULT 60\n, `version` INT NOT NULL DEFAULT 0\n, `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 0\n, `namespace_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `rule_group` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `no_data_state` VARCHAR(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'NoData\'\n, `exec_err_state` VARCHAR(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'Alerting\'\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index in alert_rule on org_id and title columns','CREATE UNIQUE INDEX `UQE_alert_rule_org_id_title` ON `alert_rule` (`org_id`,`title`);',1,'','2022-01-01 00:00:00'),
('add index in alert_rule on org_id and uid columns','CREATE UNIQUE INDEX `UQE_alert_rule_org_id_uid` ON `alert_rule` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('add index in alert_rule on org_id, namespace_uid, group_uid columns','CREATE INDEX `IDX_alert_rule_org_id_namespace_uid_rule_group` ON `alert_rule` (`org_id`,`namespace_uid`,`rule_group`);',1,'','2022-01-01 00:00:00'),
('alter alert_rule table data column to mediumtext in mysql','ALTER TABLE alert_rule MODIFY data MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('add column for to alert_rule','alter table `alert_rule` ADD COLUMN `for` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add column annotations to alert_rule','alter table `alert_rule` ADD COLUMN `annotations` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add column labels to alert_rule','alter table `alert_rule` ADD COLUMN `labels` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('remove unique index from alert_rule on org_id, title columns','DROP INDEX `UQE_alert_rule_org_id_title` ON `alert_rule`',1,'','2022-01-01 00:00:00'),
('add index in alert_rule on org_id, namespase_uid and title columns','CREATE UNIQUE INDEX `UQE_alert_rule_org_id_namespace_uid_title` ON `alert_rule` (`org_id`,`namespace_uid`,`title`);',1,'','2022-01-01 00:00:00'),
('add dashboard_uid column to alert_rule','alter table `alert_rule` ADD COLUMN `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add panel_id column to alert_rule','alter table `alert_rule` ADD COLUMN `panel_id` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('add index in alert_rule on org_id, dashboard_uid and panel_id columns','CREATE INDEX `IDX_alert_rule_org_id_dashboard_uid_panel_id` ON `alert_rule` (`org_id`,`dashboard_uid`,`panel_id`);',1,'','2022-01-01 00:00:00'),
('add rule_group_idx column to alert_rule','alter table `alert_rule` ADD COLUMN `rule_group_idx` INT NOT NULL DEFAULT 1 ',1,'','2022-01-01 00:00:00'),
('add is_paused column to alert_rule table','alter table `alert_rule` ADD COLUMN `is_paused` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('fix is_paused column for alert_rule table','',1,'','2022-01-01 00:00:00'),
('alter table alert_rule alter column rule_group_idx type to bigint','ALTER TABLE alert_rule MODIFY rule_group_idx BIGINT;',1,'','2022-01-01 00:00:00'),
('create alert_rule_version table','CREATE TABLE IF NOT EXISTS `alert_rule_version` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `rule_org_id` BIGINT(20) NOT NULL\n, `rule_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 0\n, `rule_namespace_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `rule_group` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `parent_version` INT NOT NULL\n, `restored_from` INT NOT NULL\n, `version` INT NOT NULL\n, `created` DATETIME NOT NULL\n, `title` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `condition` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `interval_seconds` BIGINT(20) NOT NULL\n, `no_data_state` VARCHAR(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'NoData\'\n, `exec_err_state` VARCHAR(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'Alerting\'\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index in alert_rule_version table on rule_org_id, rule_uid and version columns','CREATE UNIQUE INDEX `UQE_alert_rule_version_rule_org_id_rule_uid_version` ON `alert_rule_version` (`rule_org_id`,`rule_uid`,`version`);',1,'','2022-01-01 00:00:00'),
('add index in alert_rule_version table on rule_org_id, rule_namespace_uid and rule_group columns','CREATE INDEX `IDX_alert_rule_version_rule_org_id_rule_namespace_uid_rule_group` ON `alert_rule_version` (`rule_org_id`,`rule_namespace_uid`,`rule_group`);',1,'','2022-01-01 00:00:00'),
('alter alert_rule_version table data column to mediumtext in mysql','ALTER TABLE alert_rule_version MODIFY data MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('add column for to alert_rule_version','alter table `alert_rule_version` ADD COLUMN `for` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add column annotations to alert_rule_version','alter table `alert_rule_version` ADD COLUMN `annotations` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add column labels to alert_rule_version','alter table `alert_rule_version` ADD COLUMN `labels` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add rule_group_idx column to alert_rule_version','alter table `alert_rule_version` ADD COLUMN `rule_group_idx` INT NOT NULL DEFAULT 1 ',1,'','2022-01-01 00:00:00'),
('add is_paused column to alert_rule_versions table','alter table `alert_rule_version` ADD COLUMN `is_paused` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('fix is_paused column for alert_rule_version table','',1,'','2022-01-01 00:00:00'),
('alter table alert_rule_version alter column rule_group_idx type to bigint','ALTER TABLE alert_rule_version MODIFY rule_group_idx BIGINT;',1,'','2022-01-01 00:00:00'),
('create_alert_configuration_table','CREATE TABLE IF NOT EXISTS `alert_configuration` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `alertmanager_configuration` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `configuration_version` VARCHAR(3) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created_at` INT NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add column default in alert_configuration','alter table `alert_configuration` ADD COLUMN `default` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('alert alert_configuration alertmanager_configuration column from TEXT to MEDIUMTEXT if mysql','ALTER TABLE alert_configuration MODIFY alertmanager_configuration MEDIUMTEXT;',1,'','2022-01-01 00:00:00'),
('add column org_id in alert_configuration','alter table `alert_configuration` ADD COLUMN `org_id` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add index in alert_configuration table on org_id column','CREATE INDEX `IDX_alert_configuration_org_id` ON `alert_configuration` (`org_id`);',1,'','2022-01-01 00:00:00'),
('add configuration_hash column to alert_configuration','alter table `alert_configuration` ADD COLUMN `configuration_hash` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'not-yet-calculated\' ',1,'','2022-01-01 00:00:00'),
('create_ngalert_configuration_table','CREATE TABLE IF NOT EXISTS `ngalert_configuration` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `alertmanagers` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created_at` INT NOT NULL\n, `updated_at` INT NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index in ngalert_configuration on org_id column','CREATE UNIQUE INDEX `UQE_ngalert_configuration_org_id` ON `ngalert_configuration` (`org_id`);',1,'','2022-01-01 00:00:00'),
('add column send_alerts_to in ngalert_configuration','alter table `ngalert_configuration` ADD COLUMN `send_alerts_to` SMALLINT NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('create provenance_type table','CREATE TABLE IF NOT EXISTS `provenance_type` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `record_key` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `record_type` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `provenance` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index to uniquify (record_key, record_type, org_id) columns','CREATE UNIQUE INDEX `UQE_provenance_type_record_type_record_key_org_id` ON `provenance_type` (`record_type`,`record_key`,`org_id`);',1,'','2022-01-01 00:00:00'),
('create alert_image table','CREATE TABLE IF NOT EXISTS `alert_image` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `token` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `path` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `url` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created_at` DATETIME NOT NULL\n, `expires_at` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index on token to alert_image table','CREATE UNIQUE INDEX `UQE_alert_image_token` ON `alert_image` (`token`);',1,'','2022-01-01 00:00:00'),
('support longer URLs in alert_image table','ALTER TABLE alert_image MODIFY url VARCHAR(2048) NOT NULL;',1,'','2022-01-01 00:00:00'),
('create_alert_configuration_history_table','CREATE TABLE IF NOT EXISTS `alert_configuration_history` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL DEFAULT 0\n, `alertmanager_configuration` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `configuration_hash` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'not-yet-calculated\'\n, `configuration_version` VARCHAR(3) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created_at` INT NOT NULL\n, `default` TINYINT(1) NOT NULL DEFAULT 0\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('drop non-unique orgID index on alert_configuration','DROP INDEX `IDX_alert_configuration_org_id` ON `alert_configuration`',1,'','2022-01-01 00:00:00'),
('drop unique orgID index on alert_configuration if exists','DROP INDEX `UQE_alert_configuration_org_id` ON `alert_configuration`',1,'','2022-01-01 00:00:00'),
('extract alertmanager configuration history to separate table','code migration',1,'','2022-01-01 00:00:00'),
('add unique index on orgID to alert_configuration','CREATE UNIQUE INDEX `UQE_alert_configuration_org_id` ON `alert_configuration` (`org_id`);',1,'','2022-01-01 00:00:00'),
('add last_applied column to alert_configuration_history','alter table `alert_configuration_history` ADD COLUMN `last_applied` INT NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add message column to alert_rule_version','alter table `alert_rule_version` ADD COLUMN `message` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('create library_element table v1','CREATE TABLE IF NOT EXISTS `library_element` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `folder_id` BIGINT(20) NOT NULL\n, `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(150) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `kind` BIGINT(20) NOT NULL\n, `type` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `description` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `model` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `created_by` BIGINT(20) NOT NULL\n, `updated` DATETIME NOT NULL\n, `updated_by` BIGINT(20) NOT NULL\n, `version` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index library_element org_id-folder_id-name-kind','CREATE UNIQUE INDEX `UQE_library_element_org_id_folder_id_name_kind` ON `library_element` (`org_id`,`folder_id`,`name`,`kind`);',1,'','2022-01-01 00:00:00'),
('create library_element_connection table v1','CREATE TABLE IF NOT EXISTS `library_element_connection` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `element_id` BIGINT(20) NOT NULL\n, `kind` BIGINT(20) NOT NULL\n, `connection_id` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n, `created_by` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index library_element_connection element_id-kind-connection_id','CREATE UNIQUE INDEX `UQE_library_element_connection_element_id_kind_connection_id` ON `library_element_connection` (`element_id`,`kind`,`connection_id`);',1,'','2022-01-01 00:00:00'),
('add unique index library_element org_id_uid','CREATE UNIQUE INDEX `UQE_library_element_org_id_uid` ON `library_element` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('increase max description length to 2048','ALTER TABLE `library_element` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `description` VARCHAR(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('alter library_element model to mediumtext','ALTER TABLE library_element MODIFY model MEDIUMTEXT NOT NULL;',1,'','2022-01-01 00:00:00'),
('add library_element folder uid','alter table `library_element` ADD COLUMN `folder_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('populate library_element folder_uid','UPDATE library_element\n SET folder_uid = (\n SELECT dashboard.uid\n FROM dashboard\n WHERE library_element.folder_id = dashboard.id AND library_element.org_id = dashboard.org_id\n )',1,'','2022-01-01 00:00:00'),
('add index library_element org_id-folder_uid-name-kind','CREATE UNIQUE INDEX `UQE_library_element_org_id_folder_uid_name_kind` ON `library_element` (`org_id`,`folder_uid`,`name`,`kind`);',1,'','2022-01-01 00:00:00'),
('drop unique name in folder index (id)','DROP INDEX `UQE_library_element_org_id_folder_id_name_kind` ON `library_element`',1,'','2022-01-01 00:00:00'),
('drop unique name in folder index','DROP INDEX `UQE_library_element_org_id_folder_uid_name_kind` ON `library_element`',1,'','2022-01-01 00:00:00'),
('clone move dashboard alerts to unified alerting','code migration',1,'','2022-01-01 00:00:00'),
('create data_keys table','CREATE TABLE IF NOT EXISTS `data_keys` (\n`name` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `active` TINYINT(1) NOT NULL\n, `scope` VARCHAR(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `provider` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `encrypted_data` BLOB NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create secrets table','CREATE TABLE IF NOT EXISTS `secrets` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `namespace` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `value` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('rename data_keys name column to id','ALTER TABLE `data_keys` CHANGE `name` `id` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci',1,'','2022-01-01 00:00:00'),
('add name column into data_keys','alter table `data_keys` ADD COLUMN `name` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('copy data_keys id column values into name','UPDATE data_keys SET name = id',1,'','2022-01-01 00:00:00'),
('rename data_keys name column to label','ALTER TABLE `data_keys` CHANGE `name` `label` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci',1,'','2022-01-01 00:00:00'),
('rename data_keys id column back to name','ALTER TABLE `data_keys` CHANGE `id` `name` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci',1,'','2022-01-01 00:00:00'),
('create kv_store table v1','CREATE TABLE IF NOT EXISTS `kv_store` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `namespace` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `key` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `value` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index kv_store.org_id-namespace-key','CREATE UNIQUE INDEX `UQE_kv_store_org_id_namespace_key` ON `kv_store` (`org_id`,`namespace`,`key`);',1,'','2022-01-01 00:00:00'),
('update dashboard_uid and panel_id from existing annotations','set dashboard_uid and panel_id migration',1,'','2022-01-01 00:00:00'),
('create permission table','CREATE TABLE IF NOT EXISTS `permission` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `role_id` BIGINT(20) NOT NULL\n, `action` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `scope` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index permission.role_id','CREATE INDEX `IDX_permission_role_id` ON `permission` (`role_id`);',1,'','2022-01-01 00:00:00'),
('add unique index role_id_action_scope','CREATE UNIQUE INDEX `UQE_permission_role_id_action_scope` ON `permission` (`role_id`,`action`,`scope`);',1,'','2022-01-01 00:00:00'),
('create role table','CREATE TABLE IF NOT EXISTS `role` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `description` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `version` BIGINT(20) NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add column display_name','alter table `role` ADD COLUMN `display_name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add column group_name','alter table `role` ADD COLUMN `group_name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add index role.org_id','CREATE INDEX `IDX_role_org_id` ON `role` (`org_id`);',1,'','2022-01-01 00:00:00'),
('add unique index role_org_id_name','CREATE UNIQUE INDEX `UQE_role_org_id_name` ON `role` (`org_id`,`name`);',1,'','2022-01-01 00:00:00'),
('add index role_org_id_uid','CREATE UNIQUE INDEX `UQE_role_org_id_uid` ON `role` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('create team role table','CREATE TABLE IF NOT EXISTS `team_role` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `team_id` BIGINT(20) NOT NULL\n, `role_id` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index team_role.org_id','CREATE INDEX `IDX_team_role_org_id` ON `team_role` (`org_id`);',1,'','2022-01-01 00:00:00'),
('add unique index team_role_org_id_team_id_role_id','CREATE UNIQUE INDEX `UQE_team_role_org_id_team_id_role_id` ON `team_role` (`org_id`,`team_id`,`role_id`);',1,'','2022-01-01 00:00:00'),
('add index team_role.team_id','CREATE INDEX `IDX_team_role_team_id` ON `team_role` (`team_id`);',1,'','2022-01-01 00:00:00'),
('create user role table','CREATE TABLE IF NOT EXISTS `user_role` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `role_id` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index user_role.org_id','CREATE INDEX `IDX_user_role_org_id` ON `user_role` (`org_id`);',1,'','2022-01-01 00:00:00'),
('add unique index user_role_org_id_user_id_role_id','CREATE UNIQUE INDEX `UQE_user_role_org_id_user_id_role_id` ON `user_role` (`org_id`,`user_id`,`role_id`);',1,'','2022-01-01 00:00:00'),
('add index user_role.user_id','CREATE INDEX `IDX_user_role_user_id` ON `user_role` (`user_id`);',1,'','2022-01-01 00:00:00'),
('create builtin role table','CREATE TABLE IF NOT EXISTS `builtin_role` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `role` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `role_id` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index builtin_role.role_id','CREATE INDEX `IDX_builtin_role_role_id` ON `builtin_role` (`role_id`);',1,'','2022-01-01 00:00:00'),
('add index builtin_role.name','CREATE INDEX `IDX_builtin_role_role` ON `builtin_role` (`role`);',1,'','2022-01-01 00:00:00'),
('Add column org_id to builtin_role table','alter table `builtin_role` ADD COLUMN `org_id` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add index builtin_role.org_id','CREATE INDEX `IDX_builtin_role_org_id` ON `builtin_role` (`org_id`);',1,'','2022-01-01 00:00:00'),
('add unique index builtin_role_org_id_role_id_role','CREATE UNIQUE INDEX `UQE_builtin_role_org_id_role_id_role` ON `builtin_role` (`org_id`,`role_id`,`role`);',1,'','2022-01-01 00:00:00'),
('Remove unique index role_org_id_uid','DROP INDEX `UQE_role_org_id_uid` ON `role`',1,'','2022-01-01 00:00:00'),
('add unique index role.uid','CREATE UNIQUE INDEX `UQE_role_uid` ON `role` (`uid`);',1,'','2022-01-01 00:00:00'),
('create seed assignment table','CREATE TABLE IF NOT EXISTS `seed_assignment` (\n`builtin_role` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `role_name` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index builtin_role_role_name','CREATE UNIQUE INDEX `UQE_seed_assignment_builtin_role_role_name` ON `seed_assignment` (`builtin_role`,`role_name`);',1,'','2022-01-01 00:00:00'),
('add column hidden to role table','alter table `role` ADD COLUMN `hidden` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('permission kind migration','alter table `permission` ADD COLUMN `kind` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('permission attribute migration','alter table `permission` ADD COLUMN `attribute` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('permission identifier migration','alter table `permission` ADD COLUMN `identifier` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('add permission identifier index','CREATE INDEX `IDX_permission_identifier` ON `permission` (`identifier`);',1,'','2022-01-01 00:00:00'),
('add permission action scope role_id index','CREATE UNIQUE INDEX `UQE_permission_action_scope_role_id` ON `permission` (`action`,`scope`,`role_id`);',1,'','2022-01-01 00:00:00'),
('remove permission role_id action scope index','DROP INDEX `UQE_permission_role_id_action_scope` ON `permission`',1,'','2022-01-01 00:00:00'),
('add group mapping UID column to user_role table','alter table `user_role` ADD COLUMN `group_mapping_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('add user_role org ID, user ID, role ID, group mapping UID index','CREATE UNIQUE INDEX `UQE_user_role_org_id_user_id_role_id_group_mapping_uid` ON `user_role` (`org_id`,`user_id`,`role_id`,`group_mapping_uid`);',1,'','2022-01-01 00:00:00'),
('remove user_role org ID, user ID, role ID index','DROP INDEX `UQE_user_role_org_id_user_id_role_id` ON `user_role`',1,'','2022-01-01 00:00:00'),
('add permission role_id action index','CREATE INDEX `IDX_permission_role_id_action` ON `permission` (`role_id`,`action`);',1,'','2022-01-01 00:00:00'),
('Remove permission role_id index','DROP INDEX `IDX_permission_role_id` ON `permission`',1,'','2022-01-01 00:00:00'),
('create query_history table v1','CREATE TABLE IF NOT EXISTS `query_history` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `datasource_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created_by` INT NOT NULL\n, `created_at` INT NOT NULL\n, `comment` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `queries` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index query_history.org_id-created_by-datasource_uid','CREATE INDEX `IDX_query_history_org_id_created_by_datasource_uid` ON `query_history` (`org_id`,`created_by`,`datasource_uid`);',1,'','2022-01-01 00:00:00'),
('alter table query_history alter column created_by type to bigint','ALTER TABLE query_history MODIFY created_by BIGINT;',1,'','2022-01-01 00:00:00'),
('create query_history_details table v1','CREATE TABLE IF NOT EXISTS `query_history_details` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `query_history_item_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `datasource_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('rbac disabled migrator','code migration',1,'','2022-01-01 00:00:00'),
('teams permissions migration','code migration',1,'','2022-01-01 00:00:00'),
('dashboard permissions','code migration',1,'','2022-01-01 00:00:00'),
('dashboard permissions uid scopes','code migration',1,'','2022-01-01 00:00:00'),
('drop managed folder create actions','code migration',1,'','2022-01-01 00:00:00'),
('alerting notification permissions','code migration',1,'','2022-01-01 00:00:00'),
('create query_history_star table v1','CREATE TABLE IF NOT EXISTS `query_history_star` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `query_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `user_id` INT NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index query_history.user_id-query_uid','CREATE UNIQUE INDEX `UQE_query_history_star_user_id_query_uid` ON `query_history_star` (`user_id`,`query_uid`);',1,'','2022-01-01 00:00:00'),
('add column org_id in query_history_star','alter table `query_history_star` ADD COLUMN `org_id` BIGINT(20) NOT NULL DEFAULT 1 ',1,'','2022-01-01 00:00:00'),
('alter table query_history_star_mig column user_id type to bigint','ALTER TABLE query_history_star MODIFY user_id BIGINT;',1,'','2022-01-01 00:00:00'),
('create correlation table v1','CREATE TABLE IF NOT EXISTS `correlation` (\n`uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `source_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `target_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `label` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `description` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, PRIMARY KEY ( `uid`,`source_uid` )) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index correlations.uid','CREATE INDEX `IDX_correlation_uid` ON `correlation` (`uid`);',1,'','2022-01-01 00:00:00'),
('add index correlations.source_uid','CREATE INDEX `IDX_correlation_source_uid` ON `correlation` (`source_uid`);',1,'','2022-01-01 00:00:00'),
('add correlation config column','alter table `correlation` ADD COLUMN `config` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('drop index IDX_correlation_uid - v1','DROP INDEX `IDX_correlation_uid` ON `correlation`',1,'','2022-01-01 00:00:00'),
('drop index IDX_correlation_source_uid - v1','DROP INDEX `IDX_correlation_source_uid` ON `correlation`',1,'','2022-01-01 00:00:00'),
('Rename table correlation to correlation_tmp_qwerty - v1','ALTER TABLE `correlation` RENAME TO `correlation_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('create correlation v2','CREATE TABLE IF NOT EXISTS `correlation` (\n`uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL DEFAULT 0\n, `source_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `target_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `label` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `description` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `config` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, PRIMARY KEY ( `uid`,`org_id`,`source_uid` )) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index IDX_correlation_uid - v2','CREATE INDEX `IDX_correlation_uid` ON `correlation` (`uid`);',1,'','2022-01-01 00:00:00'),
('create index IDX_correlation_source_uid - v2','CREATE INDEX `IDX_correlation_source_uid` ON `correlation` (`source_uid`);',1,'','2022-01-01 00:00:00'),
('create index IDX_correlation_org_id - v2','CREATE INDEX `IDX_correlation_org_id` ON `correlation` (`org_id`);',1,'','2022-01-01 00:00:00'),
('copy correlation v1 to v2','INSERT INTO `correlation` (`target_uid`\n, `label`\n, `description`\n, `config`\n, `uid`\n, `source_uid`)\nSELECT `target_uid`\n, `label`\n, `description`\n, `config`\n, `uid`\n, `source_uid`\nFROM `correlation_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('drop correlation_tmp_qwerty','DROP TABLE IF EXISTS `correlation_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('add provisioning column','alter table `correlation` ADD COLUMN `provisioned` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add type column','alter table `correlation` ADD COLUMN `type` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'query\' ',1,'','2022-01-01 00:00:00'),
('create entity_events table','CREATE TABLE IF NOT EXISTS `entity_event` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `entity_id` VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `event_type` VARCHAR(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create dashboard public config v1','CREATE TABLE IF NOT EXISTS `dashboard_public_config` (\n`uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `time_settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `refresh_rate` INT NOT NULL DEFAULT 30\n, `template_variables` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('drop index UQE_dashboard_public_config_uid - v1','DROP INDEX `UQE_dashboard_public_config_uid` ON `dashboard_public_config`',1,'','2022-01-01 00:00:00'),
('drop index IDX_dashboard_public_config_org_id_dashboard_uid - v1','DROP INDEX `IDX_dashboard_public_config_org_id_dashboard_uid` ON `dashboard_public_config`',1,'','2022-01-01 00:00:00'),
('Drop old dashboard public config table','DROP TABLE IF EXISTS `dashboard_public_config`',1,'','2022-01-01 00:00:00'),
('recreate dashboard public config v1','CREATE TABLE IF NOT EXISTS `dashboard_public_config` (\n`uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `time_settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `refresh_rate` INT NOT NULL DEFAULT 30\n, `template_variables` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_dashboard_public_config_uid - v1','CREATE UNIQUE INDEX `UQE_dashboard_public_config_uid` ON `dashboard_public_config` (`uid`);',1,'','2022-01-01 00:00:00'),
('create index IDX_dashboard_public_config_org_id_dashboard_uid - v1','CREATE INDEX `IDX_dashboard_public_config_org_id_dashboard_uid` ON `dashboard_public_config` (`org_id`,`dashboard_uid`);',1,'','2022-01-01 00:00:00'),
('drop index UQE_dashboard_public_config_uid - v2','DROP INDEX `UQE_dashboard_public_config_uid` ON `dashboard_public_config`',1,'','2022-01-01 00:00:00'),
('drop index IDX_dashboard_public_config_org_id_dashboard_uid - v2','DROP INDEX `IDX_dashboard_public_config_org_id_dashboard_uid` ON `dashboard_public_config`',1,'','2022-01-01 00:00:00'),
('Drop public config table','DROP TABLE IF EXISTS `dashboard_public_config`',1,'','2022-01-01 00:00:00'),
('Recreate dashboard public config v2','CREATE TABLE IF NOT EXISTS `dashboard_public_config` (\n`uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `time_settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `template_variables` MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `access_token` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created_by` INT NOT NULL\n, `updated_by` INT NULL\n, `created_at` DATETIME NOT NULL\n, `updated_at` DATETIME NULL\n, `is_enabled` TINYINT(1) NOT NULL DEFAULT 0\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_dashboard_public_config_uid - v2','CREATE UNIQUE INDEX `UQE_dashboard_public_config_uid` ON `dashboard_public_config` (`uid`);',1,'','2022-01-01 00:00:00'),
('create index IDX_dashboard_public_config_org_id_dashboard_uid - v2','CREATE INDEX `IDX_dashboard_public_config_org_id_dashboard_uid` ON `dashboard_public_config` (`org_id`,`dashboard_uid`);',1,'','2022-01-01 00:00:00'),
('create index UQE_dashboard_public_config_access_token - v2','CREATE UNIQUE INDEX `UQE_dashboard_public_config_access_token` ON `dashboard_public_config` (`access_token`);',1,'','2022-01-01 00:00:00'),
('Rename table dashboard_public_config to dashboard_public - v2','ALTER TABLE `dashboard_public_config` RENAME TO `dashboard_public`',1,'','2022-01-01 00:00:00'),
('add annotations_enabled column','alter table `dashboard_public` ADD COLUMN `annotations_enabled` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add time_selection_enabled column','alter table `dashboard_public` ADD COLUMN `time_selection_enabled` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('delete orphaned public dashboards','DELETE FROM dashboard_public WHERE dashboard_uid NOT IN (SELECT uid FROM dashboard)',1,'','2022-01-01 00:00:00'),
('add share column','alter table `dashboard_public` ADD COLUMN `share` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'public\' ',1,'','2022-01-01 00:00:00'),
('backfill empty share column fields with default of public','UPDATE dashboard_public SET share=\'public\' WHERE share=\'\'',1,'','2022-01-01 00:00:00'),
('create file table','CREATE TABLE IF NOT EXISTS `file` (\n`path` VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `path_hash` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `parent_folder_path_hash` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `contents` BLOB NOT NULL\n, `etag` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `cache_control` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `content_disposition` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `updated` DATETIME NOT NULL\n, `created` DATETIME NOT NULL\n, `size` BIGINT(20) NOT NULL\n, `mime_type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('file table idx: path natural pk','CREATE UNIQUE INDEX `UQE_file_path_hash` ON `file` (`path_hash`);',1,'','2022-01-01 00:00:00'),
('file table idx: parent_folder_path_hash fast folder retrieval','CREATE INDEX `IDX_file_parent_folder_path_hash` ON `file` (`parent_folder_path_hash`);',1,'','2022-01-01 00:00:00'),
('create file_meta table','CREATE TABLE IF NOT EXISTS `file_meta` (\n`path_hash` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `key` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `value` VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('file table idx: path key','CREATE UNIQUE INDEX `UQE_file_meta_path_hash_key` ON `file_meta` (`path_hash`,`key`);',1,'','2022-01-01 00:00:00'),
('set path collation in file table','',1,'','2022-01-01 00:00:00'),
('migrate contents column to mediumblob for MySQL','ALTER TABLE file MODIFY contents MEDIUMBLOB;',1,'','2022-01-01 00:00:00'),
('drop my_row_id and add primary key to file table if my_row_id exists (auto-generated mysql column)','\n ALTER TABLE file\n DROP PRIMARY KEY,\n DROP COLUMN my_row_id,\n DROP INDEX UQE_file_path_hash,\n ADD PRIMARY KEY (path_hash);\n ',1,'','2022-01-01 00:00:00'),
('drop file_path unique index from file table if it exists (mysql)','ALTER TABLE file DROP INDEX UQE_file_path_hash',1,'','2022-01-01 00:00:00'),
('add primary key to file table if it doesn\'t exist (mysql)','ALTER TABLE file ADD PRIMARY KEY (path_hash);',1,'','2022-01-01 00:00:00'),
('add primary key to file table (postgres and sqlite)','',1,'','2022-01-01 00:00:00'),
('drop my_row_id and add primary key to file_meta table if my_row_id exists (auto-generated mysql column)','\n ALTER TABLE file_meta\n DROP PRIMARY KEY,\n DROP COLUMN my_row_id,\n DROP INDEX UQE_file_meta_path_hash_key,\n ADD PRIMARY KEY (path_hash, `key`);\n ',1,'','2022-01-01 00:00:00'),
('drop file_path unique index from file_meta table if it exists (mysql)','ALTER TABLE file_meta DROP INDEX UQE_file_meta_path_hash_key',1,'','2022-01-01 00:00:00'),
('add primary key to file_meta table if it doesn\'t exist (mysql)','ALTER TABLE file_meta ADD PRIMARY KEY (path_hash, `key`);',1,'','2022-01-01 00:00:00'),
('add primary key to file_meta table (postgres and sqlite)','',1,'','2022-01-01 00:00:00'),
('managed permissions migration','code migration',1,'','2022-01-01 00:00:00'),
('managed folder permissions alert actions migration','code migration',1,'','2022-01-01 00:00:00'),
('RBAC action name migrator','code migration',1,'','2022-01-01 00:00:00'),
('Add UID column to playlist','alter table `playlist` ADD COLUMN `uid` VARCHAR(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Update uid column values in playlist','UPDATE playlist SET uid=id;',1,'','2022-01-01 00:00:00'),
('Add index for uid in playlist','CREATE UNIQUE INDEX `UQE_playlist_org_id_uid` ON `playlist` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('update group index for alert rules','code migration',1,'','2022-01-01 00:00:00'),
('managed folder permissions alert actions repeated migration','code migration',1,'','2022-01-01 00:00:00'),
('admin only folder/dashboard permission','code migration',1,'','2022-01-01 00:00:00'),
('add action column to seed_assignment','alter table `seed_assignment` ADD COLUMN `action` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add scope column to seed_assignment','alter table `seed_assignment` ADD COLUMN `scope` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('remove unique index builtin_role_role_name before nullable update','DROP INDEX `UQE_seed_assignment_builtin_role_role_name` ON `seed_assignment`',1,'','2022-01-01 00:00:00'),
('update seed_assignment role_name column to nullable','ALTER TABLE seed_assignment MODIFY role_name VARCHAR(190) DEFAULT NULL;',1,'','2022-01-01 00:00:00'),
('add unique index builtin_role_name back','CREATE UNIQUE INDEX `UQE_seed_assignment_builtin_role_role_name` ON `seed_assignment` (`builtin_role`,`role_name`);',1,'','2022-01-01 00:00:00'),
('add unique index builtin_role_action_scope','CREATE UNIQUE INDEX `UQE_seed_assignment_builtin_role_action_scope` ON `seed_assignment` (`builtin_role`,`action`,`scope`);',1,'','2022-01-01 00:00:00'),
('add primary key to seed_assigment','code migration',1,'','2022-01-01 00:00:00'),
('add origin column to seed_assignment','alter table `seed_assignment` ADD COLUMN `origin` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add origin to plugin seed_assignment','code migration',1,'','2022-01-01 00:00:00'),
('prevent seeding OnCall access','code migration',1,'','2022-01-01 00:00:00'),
('managed folder permissions alert actions repeated fixed migration','code migration',1,'','2022-01-01 00:00:00'),
('managed folder permissions library panel actions migration','code migration',1,'','2022-01-01 00:00:00'),
('migrate external alertmanagers to datsourcse','migrate external alertmanagers to datasource',1,'','2022-01-01 00:00:00'),
('create folder table','CREATE TABLE IF NOT EXISTS `folder` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `title` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `description` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `parent_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add index for parent_uid','CREATE INDEX `IDX_folder_parent_uid_org_id` ON `folder` (`parent_uid`,`org_id`);',1,'','2022-01-01 00:00:00'),
('Add unique index for folder.uid and folder.org_id','CREATE UNIQUE INDEX `UQE_folder_uid_org_id` ON `folder` (`uid`,`org_id`);',1,'','2022-01-01 00:00:00'),
('Update folder title length','ALTER TABLE `folder` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci, MODIFY `title` VARCHAR(189) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ;',1,'','2022-01-01 00:00:00'),
('Add unique index for folder.title and folder.parent_uid','CREATE UNIQUE INDEX `UQE_folder_title_parent_uid` ON `folder` (`title`,`parent_uid`);',1,'','2022-01-01 00:00:00'),
('Remove unique index for folder.title and folder.parent_uid','DROP INDEX `UQE_folder_title_parent_uid` ON `folder`',1,'','2022-01-01 00:00:00'),
('Add unique index for title, parent_uid, and org_id','CREATE UNIQUE INDEX `UQE_folder_title_parent_uid_org_id` ON `folder` (`title`,`parent_uid`,`org_id`);',1,'','2022-01-01 00:00:00'),
('Sync dashboard and folder table','\n INSERT INTO folder (uid, org_id, title, created, updated)\n SELECT * FROM (SELECT uid, org_id, title, created, updated FROM dashboard WHERE is_folder = 1) AS derived\n ON DUPLICATE KEY UPDATE title=derived.title, updated=derived.updated\n ',1,'','2022-01-01 00:00:00'),
('Remove ghost folders from the folder table','\n DELETE FROM folder WHERE NOT EXISTS\n (SELECT 1 FROM dashboard WHERE dashboard.uid = folder.uid AND dashboard.org_id = folder.org_id AND dashboard.is_folder = true)\n ',1,'','2022-01-01 00:00:00'),
('Remove unique index UQE_folder_uid_org_id','DROP INDEX `UQE_folder_uid_org_id` ON `folder`',1,'','2022-01-01 00:00:00'),
('Add unique index UQE_folder_org_id_uid','CREATE UNIQUE INDEX `UQE_folder_org_id_uid` ON `folder` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('Remove unique index UQE_folder_title_parent_uid_org_id','DROP INDEX `UQE_folder_title_parent_uid_org_id` ON `folder`',1,'','2022-01-01 00:00:00'),
('Add unique index UQE_folder_org_id_parent_uid_title','CREATE UNIQUE INDEX `UQE_folder_org_id_parent_uid_title` ON `folder` (`org_id`,`parent_uid`,`title`);',1,'','2022-01-01 00:00:00'),
('Remove index IDX_folder_parent_uid_org_id','DROP INDEX `IDX_folder_parent_uid_org_id` ON `folder`',1,'','2022-01-01 00:00:00'),
('Remove unique index UQE_folder_org_id_parent_uid_title','DROP INDEX `UQE_folder_org_id_parent_uid_title` ON `folder`',1,'','2022-01-01 00:00:00'),
('Add index IDX_folder_org_id_parent_uid','CREATE INDEX `IDX_folder_org_id_parent_uid` ON `folder` (`org_id`,`parent_uid`);',1,'','2022-01-01 00:00:00'),
('create anon_device table','CREATE TABLE IF NOT EXISTS `anon_device` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `client_ip` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created_at` DATETIME NOT NULL\n, `device_id` VARCHAR(127) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `updated_at` DATETIME NOT NULL\n, `user_agent` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index anon_device.device_id','CREATE UNIQUE INDEX `UQE_anon_device_device_id` ON `anon_device` (`device_id`);',1,'','2022-01-01 00:00:00'),
('add index anon_device.updated_at','CREATE INDEX `IDX_anon_device_updated_at` ON `anon_device` (`updated_at`);',1,'','2022-01-01 00:00:00'),
('create signing_key table','CREATE TABLE IF NOT EXISTS `signing_key` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `key_id` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `private_key` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `added_at` DATETIME NOT NULL\n, `expires_at` DATETIME NULL\n, `alg` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index signing_key.key_id','CREATE UNIQUE INDEX `UQE_signing_key_key_id` ON `signing_key` (`key_id`);',1,'','2022-01-01 00:00:00'),
('set legacy alert migration status in kvstore','code migration',1,'','2022-01-01 00:00:00'),
('migrate record of created folders during legacy migration to kvstore','code migration',1,'','2022-01-01 00:00:00'),
('Add folder_uid for dashboard','alter table `dashboard` ADD COLUMN `folder_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Populate dashboard folder_uid column','code migration',1,'','2022-01-01 00:00:00'),
('Add unique index for dashboard_org_id_folder_uid_title','code migration',1,'','2022-01-01 00:00:00'),
('Delete unique index for dashboard_org_id_folder_id_title','DROP INDEX `UQE_dashboard_org_id_folder_id_title` ON `dashboard`',1,'','2022-01-01 00:00:00'),
('Delete unique index for dashboard_org_id_folder_uid_title','code migration',1,'','2022-01-01 00:00:00'),
('Add unique index for dashboard_org_id_folder_uid_title_is_folder','CREATE UNIQUE INDEX `UQE_dashboard_org_id_folder_uid_title_is_folder` ON `dashboard` (`org_id`,`folder_uid`,`title`,`is_folder`);',1,'','2022-01-01 00:00:00'),
('Restore index for dashboard_org_id_folder_id_title','CREATE INDEX `IDX_dashboard_org_id_folder_id_title` ON `dashboard` (`org_id`,`folder_id`,`title`);',1,'','2022-01-01 00:00:00'),
('Remove unique index for dashboard_org_id_folder_uid_title_is_folder','DROP INDEX `UQE_dashboard_org_id_folder_uid_title_is_folder` ON `dashboard`',1,'','2022-01-01 00:00:00'),
('create sso_setting table','CREATE TABLE IF NOT EXISTS `sso_setting` (\n`id` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `provider` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n, `is_deleted` TINYINT(1) NOT NULL DEFAULT 0\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('update settings column to MEDIUMTEXT','ALTER TABLE sso_setting MODIFY COLUMN settings MEDIUMTEXT NOT NULL;',1,'','2022-01-01 00:00:00'),
('copy kvstore migration status to each org','code migration',1,'','2022-01-01 00:00:00'),
('add back entry for orgid=0 migrated status','code migration',1,'','2022-01-01 00:00:00'),
('managed dashboard permissions annotation actions migration','code migration',1,'','2022-01-01 00:00:00'),
('create cloud_migration table v1','CREATE TABLE IF NOT EXISTS `cloud_migration` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `auth_token` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `stack` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create cloud_migration_run table v1','CREATE TABLE IF NOT EXISTS `cloud_migration_run` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `cloud_migration_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `result` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n, `finished` DATETIME NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add stack_id column','alter table `cloud_migration` ADD COLUMN `stack_id` BIGINT(20) NOT NULL ',1,'','2022-01-01 00:00:00'),
('add region_slug column','alter table `cloud_migration` ADD COLUMN `region_slug` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ',1,'','2022-01-01 00:00:00'),
('add cluster_slug column','alter table `cloud_migration` ADD COLUMN `cluster_slug` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ',1,'','2022-01-01 00:00:00'),
('add migration uid column','alter table `cloud_migration` ADD COLUMN `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update uid column values for migration','UPDATE cloud_migration SET uid=concat(\'u\',lpad(id,9,\'0\')) WHERE uid IS NULL;',1,'','2022-01-01 00:00:00'),
('Add unique index migration_uid','CREATE UNIQUE INDEX `UQE_cloud_migration_uid` ON `cloud_migration` (`uid`);',1,'','2022-01-01 00:00:00'),
('add migration run uid column','alter table `cloud_migration_run` ADD COLUMN `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update uid column values for migration run','UPDATE cloud_migration_run SET uid=concat(\'u\',lpad(id,9,\'0\')) WHERE uid IS NULL;',1,'','2022-01-01 00:00:00'),
('Add unique index migration_run_uid','CREATE UNIQUE INDEX `UQE_cloud_migration_run_uid` ON `cloud_migration_run` (`uid`);',1,'','2022-01-01 00:00:00'),
('Rename table cloud_migration to cloud_migration_session_tmp_qwerty - v1','ALTER TABLE `cloud_migration` RENAME TO `cloud_migration_session_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('create cloud_migration_session v2','CREATE TABLE IF NOT EXISTS `cloud_migration_session` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `auth_token` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `slug` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `stack_id` BIGINT(20) NOT NULL\n, `region_slug` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `cluster_slug` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_cloud_migration_session_uid - v2','CREATE UNIQUE INDEX `UQE_cloud_migration_session_uid` ON `cloud_migration_session` (`uid`);',1,'','2022-01-01 00:00:00'),
('copy cloud_migration_session v1 to v2','INSERT INTO `cloud_migration_session` (`id`\n, `auth_token`\n, `stack_id`\n, `cluster_slug`\n, `updated`\n, `uid`\n, `slug`\n, `region_slug`\n, `created`)\nSELECT `id`\n, `auth_token`\n, `stack_id`\n, `cluster_slug`\n, `updated`\n, `uid`\n, `stack`\n, `region_slug`\n, `created`\nFROM `cloud_migration_session_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('drop cloud_migration_session_tmp_qwerty','DROP TABLE IF EXISTS `cloud_migration_session_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('Rename table cloud_migration_run to cloud_migration_snapshot_tmp_qwerty - v1','ALTER TABLE `cloud_migration_run` RENAME TO `cloud_migration_snapshot_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('create cloud_migration_snapshot v2','CREATE TABLE IF NOT EXISTS `cloud_migration_snapshot` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `session_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `result` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n, `finished` DATETIME NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create index UQE_cloud_migration_snapshot_uid - v2','CREATE UNIQUE INDEX `UQE_cloud_migration_snapshot_uid` ON `cloud_migration_snapshot` (`uid`);',1,'','2022-01-01 00:00:00'),
('copy cloud_migration_snapshot v1 to v2','INSERT INTO `cloud_migration_snapshot` (`created`\n, `updated`\n, `finished`\n, `id`\n, `uid`\n, `session_uid`\n, `result`)\nSELECT `created`\n, `updated`\n, `finished`\n, `id`\n, `uid`\n, `cloud_migration_uid`\n, `result`\nFROM `cloud_migration_snapshot_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('drop cloud_migration_snapshot_tmp_qwerty','DROP TABLE IF EXISTS `cloud_migration_snapshot_tmp_qwerty`',1,'','2022-01-01 00:00:00'),
('add snapshot upload_url column','alter table `cloud_migration_snapshot` ADD COLUMN `upload_url` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add snapshot status column','alter table `cloud_migration_snapshot` ADD COLUMN `status` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL ',1,'','2022-01-01 00:00:00'),
('add snapshot local_directory column','alter table `cloud_migration_snapshot` ADD COLUMN `local_directory` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add snapshot gms_snapshot_uid column','alter table `cloud_migration_snapshot` ADD COLUMN `gms_snapshot_uid` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add snapshot encryption_key column','alter table `cloud_migration_snapshot` ADD COLUMN `encryption_key` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add snapshot error_string column','alter table `cloud_migration_snapshot` ADD COLUMN `error_string` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('create cloud_migration_resource table v1','CREATE TABLE IF NOT EXISTS `cloud_migration_resource` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `resource_type` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `resource_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `status` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `error_string` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `snapshot_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('delete cloud_migration_snapshot.result column','ALTER TABLE cloud_migration_snapshot DROP COLUMN result',1,'','2022-01-01 00:00:00'),
('add cloud_migration_resource.name column','alter table `cloud_migration_resource` ADD COLUMN `name` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add cloud_migration_resource.parent_name column','alter table `cloud_migration_resource` ADD COLUMN `parent_name` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add cloud_migration_session.org_id column','alter table `cloud_migration_session` ADD COLUMN `org_id` BIGINT(20) NOT NULL DEFAULT 1 ',1,'','2022-01-01 00:00:00'),
('add cloud_migration_resource.error_code column','alter table `cloud_migration_resource` ADD COLUMN `error_code` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('increase resource_uid column length','ALTER TABLE cloud_migration_resource MODIFY resource_uid NVARCHAR(255);',1,'','2022-01-01 00:00:00'),
('create cloud_migration_snapshot_partition table v1','CREATE TABLE IF NOT EXISTS `cloud_migration_snapshot_partition` (\n`snapshot_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `partition_number` INT NOT NULL\n, `resource_type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data` LONGBLOB NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add cloud_migration_snapshot_partition srp_unique index','CREATE UNIQUE INDEX `UQE_cloud_migration_snapshot_partition_srp_unique` ON `cloud_migration_snapshot_partition` (`snapshot_uid`,`resource_type`,`partition_number`);',1,'','2022-01-01 00:00:00'),
('add resource_storage_type column to cloud_migration_snapshot table','alter table `cloud_migration_snapshot` ADD COLUMN `resource_storage_type` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add encryption_algo column to cloud_migration_snapshot table','alter table `cloud_migration_snapshot` ADD COLUMN `encryption_algo` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add metadata column to cloud_migration_snapshot table','alter table `cloud_migration_snapshot` ADD COLUMN `metadata` BLOB NULL ',1,'','2022-01-01 00:00:00'),
('add public_key column to cloud_migration_snapshot table','alter table `cloud_migration_snapshot` ADD COLUMN `public_key` BLOB NULL ',1,'','2022-01-01 00:00:00'),
('drop my_row_id and add primary key with columns snapshot_uid,resource_type,partition_number to table cloud_migration_snapshot_partition if my_row_id exists (auto-generated mysql column)','\n ALTER TABLE cloud_migration_snapshot_partition\n DROP PRIMARY KEY,\n DROP COLUMN my_row_id,\n DROP INDEX UQE_cloud_migration_snapshot_partition_srp_unique,\n ADD PRIMARY KEY (`snapshot_uid`,`resource_type`,`partition_number`);\n ',1,'','2022-01-01 00:00:00'),
('drop unique index UQE_cloud_migration_snapshot_partition_srp_unique from cloud_migration_snapshot_partition table if it exists (mysql)','ALTER TABLE cloud_migration_snapshot_partition DROP INDEX UQE_cloud_migration_snapshot_partition_srp_unique',1,'','2022-01-01 00:00:00'),
('add primary key with columns snapshot_uid,resource_type,partition_number to table cloud_migration_snapshot_partition if it doesn\'t exist (mysql)','ALTER TABLE cloud_migration_snapshot_partition ADD PRIMARY KEY (`snapshot_uid`,`resource_type`,`partition_number`)',1,'','2022-01-01 00:00:00'),
('add primary key with columns snapshot_uid,resource_type,partition_number to table cloud_migration_snapshot_partition (postgres and sqlite)','',1,'','2022-01-01 00:00:00'),
('alter kv_store.value to longtext','ALTER TABLE kv_store MODIFY value LONGTEXT NOT NULL;',1,'','2022-01-01 00:00:00'),
('add notification_settings column to alert_rule table','alter table `alert_rule` ADD COLUMN `notification_settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add notification_settings column to alert_rule_version table','alter table `alert_rule_version` ADD COLUMN `notification_settings` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('removing scope from alert.instances:read action migration','code migration',1,'','2022-01-01 00:00:00'),
('managed folder permissions alerting silences actions migration','code migration',1,'','2022-01-01 00:00:00'),
('add record column to alert_rule table','alter table `alert_rule` ADD COLUMN `record` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add record column to alert_rule_version table','alter table `alert_rule_version` ADD COLUMN `record` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add resolved_at column to alert_instance table','alter table `alert_instance` ADD COLUMN `resolved_at` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('add last_sent_at column to alert_instance table','alter table `alert_instance` ADD COLUMN `last_sent_at` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('Add scope to alert.notifications.receivers:read and alert.notifications.receivers.secrets:read','code migration',1,'','2022-01-01 00:00:00'),
('add metadata column to alert_rule table','alter table `alert_rule` ADD COLUMN `metadata` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add metadata column to alert_rule_version table','alter table `alert_rule_version` ADD COLUMN `metadata` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('delete orphaned service account permissions','code migration',1,'','2022-01-01 00:00:00'),
('adding action set permissions','code migration',1,'','2022-01-01 00:00:00'),
('create user_external_session table','CREATE TABLE IF NOT EXISTS `user_external_session` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `user_auth_id` BIGINT(20) NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `auth_module` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `access_token` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `id_token` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `refresh_token` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `session_id` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `session_id_hash` CHAR(44) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `name_id` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `name_id_hash` CHAR(44) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `expires_at` DATETIME NULL\n, `created_at` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('increase name_id column length to 1024','ALTER TABLE user_external_session MODIFY name_id NVARCHAR(1024);',1,'','2022-01-01 00:00:00'),
('increase session_id column length to 1024','ALTER TABLE user_external_session MODIFY session_id NVARCHAR(1024);',1,'','2022-01-01 00:00:00'),
('remove scope from alert.notifications.receivers:create','code migration',1,'','2022-01-01 00:00:00'),
('add created_by column to alert_rule_version table','alter table `alert_rule_version` ADD COLUMN `created_by` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add updated_by column to alert_rule table','alter table `alert_rule` ADD COLUMN `updated_by` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add alert_rule_state table','CREATE TABLE IF NOT EXISTS `alert_rule_state` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `rule_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data` LONGBLOB NOT NULL\n, `updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index to alert_rule_state on org_id and rule_uid columns','CREATE UNIQUE INDEX `UQE_alert_rule_state_org_id_rule_uid` ON `alert_rule_state` (`org_id`,`rule_uid`);',1,'','2022-01-01 00:00:00'),
('add guid column to alert_rule table','alter table `alert_rule` ADD COLUMN `guid` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('add rule_guid column to alert_rule_version table','alter table `alert_rule_version` ADD COLUMN `rule_guid` VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'\' ',1,'','2022-01-01 00:00:00'),
('cleanup alert_rule_version table','code migration',1,'','2022-01-01 00:00:00'),
('populate rule guid in alert rule table','code migration',1,'','2022-01-01 00:00:00'),
('drop index in alert_rule_version table on rule_org_id, rule_uid and version columns','DROP INDEX `UQE_alert_rule_version_rule_org_id_rule_uid_version` ON `alert_rule_version`',1,'','2022-01-01 00:00:00'),
('add index in alert_rule_version table on rule_org_id, rule_uid, rule_guid and version columns','CREATE UNIQUE INDEX `UQE_alert_rule_version_rule_org_id_rule_uid_rule_guid_version` ON `alert_rule_version` (`rule_org_id`,`rule_uid`,`rule_guid`,`version`);',1,'','2022-01-01 00:00:00'),
('add index in alert_rule_version table on rule_guid and version columns','CREATE UNIQUE INDEX `UQE_alert_rule_version_rule_guid_version` ON `alert_rule_version` (`rule_guid`,`version`);',1,'','2022-01-01 00:00:00'),
('add index in alert_rule table on guid columns','CREATE UNIQUE INDEX `UQE_alert_rule_guid` ON `alert_rule` (`guid`);',1,'','2022-01-01 00:00:00'),
('add keep_firing_for column to alert_rule','alter table `alert_rule` ADD COLUMN `keep_firing_for` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add keep_firing_for column to alert_rule_version','alter table `alert_rule_version` ADD COLUMN `keep_firing_for` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add missing_series_evals_to_resolve column to alert_rule','alter table `alert_rule` ADD COLUMN `missing_series_evals_to_resolve` SMALLINT NULL ',1,'','2022-01-01 00:00:00'),
('add missing_series_evals_to_resolve column to alert_rule_version','alter table `alert_rule_version` ADD COLUMN `missing_series_evals_to_resolve` SMALLINT NULL ',1,'','2022-01-01 00:00:00'),
('remove the datasources:drilldown action','code migration',1,'','2022-01-01 00:00:00'),
('remove title in folder unique index','DROP INDEX `UQE_alert_rule_org_id_namespace_uid_title` ON `alert_rule`',1,'','2022-01-01 00:00:00'),
('add fired_at column to alert_instance table','alter table `alert_instance` ADD COLUMN `fired_at` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('ensure rule_group column is case sensitive in returned results','ALTER TABLE alert_rule MODIFY rule_group VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs NOT NULL;',1,'','2022-01-01 00:00:00'),
('expand created_by in alert_rule_version table','ALTER TABLE `alert_rule_version` MODIFY created_by VARCHAR(190);',1,'','2022-01-01 00:00:00'),
('expand updated_by in alert_rule table','ALTER TABLE `alert_rule` MODIFY updated_by VARCHAR(190);',1,'','2022-01-01 00:00:00'),
('add index in alert_rule on org_id, namespace_uid, rule_group and rule_group_idx columns','CREATE INDEX `IDX_alert_rule_org_id_namespace_uid_rule_group_rule_group_idx` ON `alert_rule` (`org_id`,`namespace_uid`,`rule_group`,`rule_group_idx`);',1,'','2022-01-01 00:00:00'),
('add annotations column to alert_instance table','alter table `alert_instance` ADD COLUMN `annotations` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('ensure namespace_uid column sorts the same way as golang','ALTER TABLE alert_rule MODIFY namespace_uid VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL;',1,'','2022-01-01 00:00:00'),
('ensure rule_group column sorts the same way as golang','ALTER TABLE alert_rule MODIFY rule_group VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL;',1,'','2022-01-01 00:00:00'),
('add \'alert.notifications.receivers.protected:write\' to receiver admins','code migration',1,'','2022-01-01 00:00:00'),
('create data_source_usage_by_day table','CREATE TABLE IF NOT EXISTS `data_source_usage_by_day` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `data_source_id` BIGINT(20) NOT NULL\n, `day` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `queries` BIGINT(20) NOT NULL\n, `errors` BIGINT(20) NOT NULL\n, `load_duration_ms` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create data_source_usage_by_day(data_source_id) index','CREATE INDEX `IDX_data_source_usage_by_day_data_source_id` ON `data_source_usage_by_day` (`data_source_id`);',1,'','2022-01-01 00:00:00'),
('create data_source_usage_by_day(data_source_id, day) unique index','CREATE UNIQUE INDEX `UQE_data_source_usage_by_day_data_source_id_day` ON `data_source_usage_by_day` (`data_source_id`,`day`);',1,'','2022-01-01 00:00:00'),
('create dashboard_usage_by_day table','CREATE TABLE IF NOT EXISTS `dashboard_usage_by_day` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `dashboard_id` BIGINT(20) NOT NULL\n, `day` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `views` BIGINT(20) NOT NULL\n, `queries` BIGINT(20) NOT NULL\n, `errors` BIGINT(20) NOT NULL\n, `load_duration` FLOAT NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create dashboard_usage_sums table','CREATE TABLE IF NOT EXISTS `dashboard_usage_sums` (\n`dashboard_id` BIGINT(20) PRIMARY KEY NOT NULL\n, `updated` DATETIME NOT NULL\n, `views_last_1_days` BIGINT(20) NOT NULL\n, `views_last_7_days` BIGINT(20) NOT NULL\n, `views_last_30_days` BIGINT(20) NOT NULL\n, `views_total` BIGINT(20) NOT NULL\n, `queries_last_1_days` BIGINT(20) NOT NULL\n, `queries_last_7_days` BIGINT(20) NOT NULL\n, `queries_last_30_days` BIGINT(20) NOT NULL\n, `queries_total` BIGINT(20) NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create dashboard_usage_by_day(dashboard_id) index','CREATE INDEX `IDX_dashboard_usage_by_day_dashboard_id` ON `dashboard_usage_by_day` (`dashboard_id`);',1,'','2022-01-01 00:00:00'),
('create dashboard_usage_by_day(dashboard_id, day) index','CREATE UNIQUE INDEX `UQE_dashboard_usage_by_day_dashboard_id_day` ON `dashboard_usage_by_day` (`dashboard_id`,`day`);',1,'','2022-01-01 00:00:00'),
('add column errors_last_1_days to dashboard_usage_sums','alter table `dashboard_usage_sums` ADD COLUMN `errors_last_1_days` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add column errors_last_7_days to dashboard_usage_sums','alter table `dashboard_usage_sums` ADD COLUMN `errors_last_7_days` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add column errors_last_30_days to dashboard_usage_sums','alter table `dashboard_usage_sums` ADD COLUMN `errors_last_30_days` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add column errors_total to dashboard_usage_sums','alter table `dashboard_usage_sums` ADD COLUMN `errors_total` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('create dashboard_public_usage_by_day table','CREATE TABLE IF NOT EXISTS `dashboard_public_usage_by_day` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `public_dashboard_uid` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `day` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `views` BIGINT(20) NOT NULL\n, `queries` BIGINT(20) NOT NULL\n, `errors` BIGINT(20) NOT NULL\n, `load_duration` FLOAT NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add column cached_queries to dashboard_usage_by_day table','alter table `dashboard_usage_by_day` ADD COLUMN `cached_queries` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add column cached_queries to dashboard_public_usage_by_day table','alter table `dashboard_public_usage_by_day` ADD COLUMN `cached_queries` BIGINT(20) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('add column dashboard_uid to dashboard_usage_sums','alter table `dashboard_usage_sums` ADD COLUMN `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add column org_id to dashboard_usage_sums','alter table `dashboard_usage_sums` ADD COLUMN `org_id` BIGINT(20) NULL DEFAULT 1 ',1,'','2022-01-01 00:00:00'),
('add column dashboard_uid to dashboard_usage_by_day','alter table `dashboard_usage_by_day` ADD COLUMN `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add column org_id to dashboard_usage_by_day','alter table `dashboard_usage_by_day` ADD COLUMN `org_id` BIGINT(20) NULL DEFAULT 1 ',1,'','2022-01-01 00:00:00'),
('create dashboard_usage_by_day(dashboard_uid, org_id, day) unique index','CREATE UNIQUE INDEX `UQE_dashboard_usage_by_day_dashboard_uid_org_id_day` ON `dashboard_usage_by_day` (`dashboard_uid`,`org_id`,`day`);',1,'','2022-01-01 00:00:00'),
('Add missing dashboard_uid and org_id to dashboard_usage_by_day and dashboard_usage_sums','code migration',1,'','2022-01-01 00:00:00'),
('Add dashboard_usage_sums(org_id, dashboard_uid) index','CREATE UNIQUE INDEX `UQE_dashboard_usage_sums_org_id_dashboard_uid` ON `dashboard_usage_sums` (`org_id`,`dashboard_uid`);',1,'','2022-01-01 00:00:00'),
('create user_dashboard_views table','CREATE TABLE IF NOT EXISTS `user_dashboard_views` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `dashboard_id` BIGINT(20) NOT NULL\n, `viewed` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index user_dashboard_views.user_id','CREATE INDEX `IDX_user_dashboard_views_user_id` ON `user_dashboard_views` (`user_id`);',1,'','2022-01-01 00:00:00'),
('add index user_dashboard_views.dashboard_id','CREATE INDEX `IDX_user_dashboard_views_dashboard_id` ON `user_dashboard_views` (`dashboard_id`);',1,'','2022-01-01 00:00:00'),
('add unique index user_dashboard_views_user_id_dashboard_id','CREATE UNIQUE INDEX `UQE_user_dashboard_views_user_id_dashboard_id` ON `user_dashboard_views` (`user_id`,`dashboard_id`);',1,'','2022-01-01 00:00:00'),
('add org_id column to user_dashboard_views','alter table `user_dashboard_views` ADD COLUMN `org_id` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('add dashboard_uid column to user_dashboard_views','alter table `user_dashboard_views` ADD COLUMN `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('add unique index user_dashboard_views_org_id_dashboard_uid','CREATE INDEX `IDX_user_dashboard_views_org_id_dashboard_uid` ON `user_dashboard_views` (`org_id`,`dashboard_uid`);',1,'','2022-01-01 00:00:00'),
('add unique index user_dashboard_views_org_id_user_id_dashboard_uid','CREATE UNIQUE INDEX `UQE_user_dashboard_views_user_id_org_id_dashboard_uid` ON `user_dashboard_views` (`user_id`,`org_id`,`dashboard_uid`);',1,'','2022-01-01 00:00:00'),
('populate user_dashboard_views.dashboard_uid and user_dashboard_views.org_id from dashboard table','views migration',1,'','2022-01-01 00:00:00'),
('delete views where dashboard does not exist','DELETE FROM user_dashboard_views WHERE (dashboard_uid IS NULL OR org_id IS NULL) AND NOT EXISTS (SELECT 1 FROM dashboard WHERE dashboard.id = user_dashboard_views.dashboard_id)',1,'','2022-01-01 00:00:00'),
('create user_stats table','CREATE TABLE IF NOT EXISTS `user_stats` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `billing_role` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index user_stats(user_id)','CREATE UNIQUE INDEX `UQE_user_stats_user_id` ON `user_stats` (`user_id`);',1,'','2022-01-01 00:00:00'),
('create data_source_cache table','CREATE TABLE IF NOT EXISTS `data_source_cache` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `data_source_id` BIGINT(20) NOT NULL\n, `enabled` TINYINT(1) NOT NULL\n, `ttl_ms` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index data_source_cache.data_source_id','CREATE INDEX `IDX_data_source_cache_data_source_id` ON `data_source_cache` (`data_source_id`);',1,'','2022-01-01 00:00:00'),
('add use_default_ttl column','alter table `data_source_cache` ADD COLUMN `use_default_ttl` TINYINT(1) NOT NULL DEFAULT 1 ',1,'','2022-01-01 00:00:00'),
('add data_source_cache.data_source_uid column','alter table `data_source_cache` ADD COLUMN `data_source_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('remove abandoned data_source_cache records','DELETE FROM data_source_cache WHERE NOT EXISTS (SELECT 1 FROM data_source WHERE id = data_source_cache.data_source_id);',1,'','2022-01-01 00:00:00'),
('update data_source_cache.data_source_uid value','UPDATE data_source_cache JOIN data_source ON data_source_cache.data_source_id = data_source.id SET data_source_cache.data_source_uid = data_source.uid;',1,'','2022-01-01 00:00:00'),
('add index data_source_cache.data_source_uid','CREATE INDEX `IDX_data_source_cache_data_source_uid` ON `data_source_cache` (`data_source_uid`);',1,'','2022-01-01 00:00:00'),
('add data_source_cache.ttl_resources_ms column','alter table `data_source_cache` ADD COLUMN `ttl_resources_ms` BIGINT(20) NOT NULL DEFAULT 300000 ',1,'','2022-01-01 00:00:00'),
('update data_source_cache.ttl_resources_ms to have the same value as ttl_ms','UPDATE data_source_cache SET ttl_resources_ms = ttl_ms;',1,'','2022-01-01 00:00:00'),
('create data_source_acl table','CREATE TABLE IF NOT EXISTS `data_source_acl` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `data_source_id` BIGINT(20) NOT NULL\n, `team_id` BIGINT(20) NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `permission` SMALLINT NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index data_source_acl.data_source_id','CREATE INDEX `IDX_data_source_acl_data_source_id` ON `data_source_acl` (`data_source_id`);',1,'','2022-01-01 00:00:00'),
('add unique index datasource_acl.unique','CREATE UNIQUE INDEX `UQE_data_source_acl_data_source_id_team_id_user_id` ON `data_source_acl` (`data_source_id`,`team_id`,`user_id`);',1,'','2022-01-01 00:00:00'),
('create license_token table','CREATE TABLE IF NOT EXISTS `license_token` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `token` LONGTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('drop recorded_queries table v14','DROP TABLE IF EXISTS `recorded_queries`',1,'','2022-01-01 00:00:00'),
('drop recording_rules table v14','DROP TABLE IF EXISTS `recording_rules`',1,'','2022-01-01 00:00:00'),
('create recording_rules table v14','CREATE TABLE IF NOT EXISTS `recording_rules` (\n`id` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `target_ref_id` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `name` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `description` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `interval` BIGINT(20) NOT NULL\n, `range` BIGINT(20) NOT NULL\n, `active` TINYINT(1) NOT NULL DEFAULT 0\n, `count` TINYINT(1) NOT NULL DEFAULT 0\n, `queries` BLOB NOT NULL\n, `created_at` DATETIME NOT NULL\n, PRIMARY KEY ( `id`,`target_ref_id` )) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create remote_write_targets table v1','CREATE TABLE IF NOT EXISTS `remote_write_targets` (\n`id` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data_source_uid` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `write_path` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` INT NOT NULL\n, PRIMARY KEY ( `id`,`data_source_uid`,`write_path` )) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add prom_name to recording_rules table','alter table `recording_rules` ADD COLUMN `prom_name` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('ensure remote_write_targets table','CREATE TABLE IF NOT EXISTS `remote_write_targets` (\n`id` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `data_source_uid` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `write_path` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `org_id` INT NOT NULL\n, PRIMARY KEY ( `id`,`data_source_uid`,`write_path` )) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create report config table v1','CREATE TABLE IF NOT EXISTS `report` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `dashboard_id` BIGINT(20) NOT NULL\n, `name` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `recipients` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `reply_to` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `message` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `schedule_frequency` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `schedule_day` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `schedule_hour` BIGINT(20) NOT NULL\n, `schedule_minute` BIGINT(20) NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add index report.user_id','CREATE INDEX `IDX_report_user_id` ON `report` (`user_id`);',1,'','2022-01-01 00:00:00'),
('add index to dashboard_id','CREATE INDEX `IDX_report_dashboard_id` ON `report` (`dashboard_id`);',1,'','2022-01-01 00:00:00'),
('add index to org_id','CREATE INDEX `IDX_report_org_id` ON `report` (`org_id`);',1,'','2022-01-01 00:00:00'),
('Add timezone to the report','alter table `report` ADD COLUMN `schedule_timezone` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'Europe/Stockholm\' ',1,'','2022-01-01 00:00:00'),
('Add time_from to the report','alter table `report` ADD COLUMN `time_from` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add time_to to the report','alter table `report` ADD COLUMN `time_to` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add PDF landscape option to the report','alter table `report` ADD COLUMN `pdf_landscape` TINYINT(1) NULL ',1,'','2022-01-01 00:00:00'),
('Add monthly day scheduling option to the report','alter table `report` ADD COLUMN `schedule_day_of_month` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add PDF layout option to the report','alter table `report` ADD COLUMN `pdf_layout` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add PDF orientation option to the report','alter table `report` ADD COLUMN `pdf_orientation` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update report pdf_orientation from pdf_landscape','UPDATE report SET pdf_orientation = \'landscape\' WHERE pdf_landscape = 1',1,'','2022-01-01 00:00:00'),
('create report settings table','CREATE TABLE IF NOT EXISTS `report_settings` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `user_id` BIGINT(20) NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `branding_report_logo_url` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `branding_email_logo_url` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `branding_email_footer_link` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `branding_email_footer_text` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `branding_email_footer_mode` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add dashboard_uid field to the report','alter table `report` ADD COLUMN `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add template_vars field to the report','alter table `report` ADD COLUMN `template_vars` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add option to include dashboard url in the report','alter table `report` ADD COLUMN `enable_dashboard_url` TINYINT(1) NULL ',1,'','2022-01-01 00:00:00'),
('Add state field to the report','alter table `report` ADD COLUMN `state` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add option to add CSV files to the report','alter table `report` ADD COLUMN `enable_csv` TINYINT(1) NULL ',1,'','2022-01-01 00:00:00'),
('Add scheduling start date','alter table `report` ADD COLUMN `schedule_start` INT NULL ',1,'','2022-01-01 00:00:00'),
('Add missing schedule_start date for old reports','code migration',1,'','2022-01-01 00:00:00'),
('Add scheduling end date','alter table `report` ADD COLUMN `schedule_end` INT NULL ',1,'','2022-01-01 00:00:00'),
('Add schedulinng custom interval frequency','alter table `report` ADD COLUMN `schedule_interval_frequency` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add scheduling custom interval amount','alter table `report` ADD COLUMN `schedule_interval_amount` BIGINT(20) NULL ',1,'','2022-01-01 00:00:00'),
('Add workdays only flag to report','alter table `report` ADD COLUMN `schedule_workdays_only` TINYINT(1) NULL ',1,'','2022-01-01 00:00:00'),
('create report dashboards table','CREATE TABLE IF NOT EXISTS `report_dashboards` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `report_id` BIGINT(20) NOT NULL\n, `dashboard_uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'\'\n, `report_variables` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `time_to` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n, `time_from` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('Add index report_dashboards.report_id','CREATE INDEX `IDX_report_dashboards_report_id` ON `report_dashboards` (`report_id`);',1,'','2022-01-01 00:00:00'),
('Migrate report fields into report_dashboards','INSERT INTO report_dashboards (report_id, dashboard_uid, report_variables, time_to, time_from) SELECT id, CASE WHEN dashboard_uid IS NULL THEN \'\' ELSE dashboard_uid END, template_vars, time_to, time_from FROM report',1,'','2022-01-01 00:00:00'),
('Add formats option to the report','alter table `report` ADD COLUMN `formats` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'[\"pdf\"]\' ',1,'','2022-01-01 00:00:00'),
('Migrate reports with csv enabled','UPDATE report SET formats=\'[\"pdf\",\"csv\"]\' WHERE enable_csv IS TRUE;',1,'','2022-01-01 00:00:00'),
('Migrate ancient reports','code migration',1,'','2022-01-01 00:00:00'),
('Add created column in report_dashboards','alter table `report_dashboards` ADD COLUMN `created` DATETIME NULL ',1,'','2022-01-01 00:00:00'),
('Add scale_factor to the report','alter table `report` ADD COLUMN `scale_factor` TINYINT NOT NULL DEFAULT 2 ',1,'','2022-01-01 00:00:00'),
('Alter scale_factor from TINYINT to SMALLINT','ALTER TABLE report MODIFY scale_factor SMALLINT NOT NULL DEFAULT 2',1,'','2022-01-01 00:00:00'),
('Add uid column to report','alter table `report` ADD COLUMN `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Add unique index reports_org_id_uid','CREATE UNIQUE INDEX `UQE_report_org_id_uid` ON `report` (`org_id`,`uid`);',1,'','2022-01-01 00:00:00'),
('Add pdf show template variable values to the report','alter table `report` ADD COLUMN `pdf_show_template_variables` TINYINT(1) NOT NULL DEFAULT 0 ',1,'','2022-01-01 00:00:00'),
('Add pdf combine in one file','alter table `report` ADD COLUMN `pdf_combine_one_file` TINYINT(1) NOT NULL DEFAULT 1 ',1,'','2022-01-01 00:00:00'),
('Add pdf theme to report settings table','alter table `report_settings` ADD COLUMN `pdf_theme` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'light\' ',1,'','2022-01-01 00:00:00'),
('Add email subject to the report','alter table `report` ADD COLUMN `subject` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Populate email subject with report name','UPDATE report SET subject = name WHERE subject IS NULL',1,'','2022-01-01 00:00:00'),
('Add embedded image theme to report settings table','alter table `report_settings` ADD COLUMN `embedded_image_theme` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'dark\' ',1,'','2022-01-01 00:00:00'),
('create team group table','CREATE TABLE IF NOT EXISTS `team_group` (\n`id` BIGINT(20) PRIMARY KEY AUTO_INCREMENT NOT NULL\n, `org_id` BIGINT(20) NOT NULL\n, `team_id` BIGINT(20) NOT NULL\n, `group_id` VARCHAR(190) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created` DATETIME NOT NULL\n, `updated` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add index team_group.org_id','CREATE INDEX `IDX_team_group_org_id` ON `team_group` (`org_id`);',1,'','2022-01-01 00:00:00'),
('add unique index team_group.org_id_team_id_group_id','CREATE UNIQUE INDEX `UQE_team_group_org_id_team_id_group_id` ON `team_group` (`org_id`,`team_id`,`group_id`);',1,'','2022-01-01 00:00:00'),
('add index team_group.group_id','CREATE INDEX `IDX_team_group_group_id` ON `team_group` (`group_id`);',1,'','2022-01-01 00:00:00'),
('Add column uid in team_group','alter table `team_group` ADD COLUMN `uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('Update uid column values in team_group','UPDATE team_group SET uid=concat(\'tg\',lpad(id,9,\'0\')) WHERE uid IS NULL OR uid = \'\';',1,'','2022-01-01 00:00:00'),
('Add unique index team_group_uid','CREATE UNIQUE INDEX `UQE_team_group_uid` ON `team_group` (`uid`);',1,'','2022-01-01 00:00:00'),
('create settings table','CREATE TABLE IF NOT EXISTS `setting` (\n`section` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `key` VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `value` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add unique index settings.section_key','CREATE UNIQUE INDEX `UQE_setting_section_key` ON `setting` (`section`,`key`);',1,'','2022-01-01 00:00:00'),
('add setting.encrypted_value','alter table `setting` ADD COLUMN `encrypted_value` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL ',1,'','2022-01-01 00:00:00'),
('drop my_row_id and add primary key to setting table if my_row_id exists (auto-generated mysql column)','\n ALTER TABLE setting\n DROP PRIMARY KEY,\n DROP COLUMN my_row_id,\n DROP INDEX UQE_setting_section_key,\n ADD PRIMARY KEY (section, `key`);\n ',1,'','2022-01-01 00:00:00'),
('drop setting_section unique index from setting table if it exists (mysql)','ALTER TABLE setting DROP INDEX UQE_setting_section_key',1,'','2022-01-01 00:00:00'),
('add primary key to setting table if it doesn\'t exist (mysql)','ALTER TABLE setting ADD PRIMARY KEY (section, `key`);',1,'','2022-01-01 00:00:00'),
('add primary key to setting table (postgres and sqlite)','',1,'','2022-01-01 00:00:00'),
('migrate role names','code migration',1,'','2022-01-01 00:00:00'),
('rename orgs roles','code migration',1,'','2022-01-01 00:00:00'),
('remove duplicated org role','code migration',1,'','2022-01-01 00:00:00'),
('migrate alerting role names','code migration',1,'','2022-01-01 00:00:00'),
('data source permissions','code migration',1,'','2022-01-01 00:00:00'),
('data source uid permissions','code migration',1,'','2022-01-01 00:00:00'),
('rename permissions:delegate scope','UPDATE permission SET scope = \'permissions:type:delegate\' WHERE scope = \'permissions:delegate\'',1,'','2022-01-01 00:00:00'),
('remove invalid managed permissions','code migration',1,'','2022-01-01 00:00:00'),
('builtin role migration','code migration',1,'','2022-01-01 00:00:00'),
('seed permissions migration','code migration',1,'','2022-01-01 00:00:00'),
('managed permissions migration enterprise','code migration',1,'','2022-01-01 00:00:00'),
('create table dashboard_public_email_share','CREATE TABLE IF NOT EXISTS `dashboard_public_email_share` (\n`uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `public_dashboard_uid` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `recipient` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `type` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT \'email\'\n, `created_at` DATETIME NOT NULL\n, `updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create table dashboard_public_magic_link','CREATE TABLE IF NOT EXISTS `dashboard_public_magic_link` (\n`uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `token_uuid` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `public_dashboard_uid` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `email` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created_at` DATETIME NOT NULL\n, `updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('create table dashboard_public_session','CREATE TABLE IF NOT EXISTS `dashboard_public_session` (\n`uid` VARCHAR(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci PRIMARY KEY NOT NULL\n, `cookie_uuid` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `public_dashboard_uid` VARCHAR(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `email` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL\n, `created_at` DATETIME NOT NULL\n, `updated_at` DATETIME NOT NULL\n) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;',1,'','2022-01-01 00:00:00'),
('add last_seen_at column','alter table `dashboard_public_session` ADD COLUMN `last_seen_at` DATETIME NULL ',1,'','2022-01-01 00:00:00');
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
@@ -0,0 +1,9 @@
SELECT {{ .Ident "key_path" }}
FROM {{ .TableName }}
WHERE {{ .Ident "key_path" }} >= {{ .Arg .StartKey }}
AND {{ .Ident "key_path" }} < {{ .Arg .EndKey }}
ORDER BY {{ .Ident "key_path" }} {{ if .SortAscending }}ASC{{ else }}DESC{{ end }}
{{ if .Options.Limit }}
LIMIT {{ .Options.Limit }}
{{ end }}
;
+53 -1
View File
@@ -9,6 +9,7 @@ import (
"fmt"
"io"
"iter"
"strings"
"text/template"
"github.com/grafana/grafana/pkg/storage/unified/sql/db"
@@ -35,6 +36,7 @@ func mustTemplate(filename string) *template.Template {
var (
sqlKVGet = mustTemplate("sqlkv_get.sql")
sqlKVDelete = mustTemplate("sqlkv_delete.sql")
sqlKVKeys = mustTemplate("sqlkv_keys.sql")
)
// sqlKVSection can be embedded in structs used when rendering query templates
@@ -114,6 +116,32 @@ func (req sqlKVDeleteRequest) Validate() error {
return req.sqlKVSectionKey.Validate()
}
type sqlKVKeysRequest struct {
sqltemplate.SQLTemplate
sqlKVSection
Options ListOptions
}
func (req sqlKVKeysRequest) Validate() error {
return req.sqlKVSection.Validate()
}
func (req sqlKVKeysRequest) StartKey() string {
return req.Section + "/" + req.Options.StartKey
}
func (req sqlKVKeysRequest) EndKey() string {
if req.Options.EndKey == "" {
req.Options.EndKey = PrefixRangeEnd(req.Section + "/")
}
return req.Section + "/" + req.Options.EndKey
}
func (req sqlKVKeysRequest) SortAscending() bool {
return req.Options.Sort != SortOrderDesc
}
var _ KV = &sqlKV{}
type sqlKV struct {
@@ -151,7 +179,31 @@ func (k *sqlKV) Ping(ctx context.Context) error {
func (k *sqlKV) Keys(ctx context.Context, section string, opt ListOptions) iter.Seq2[string, error] {
return func(yield func(string, error) bool) {
panic("not implemented!")
rows, err := dbutil.QueryRows(ctx, k.db, sqlKVKeys, sqlKVKeysRequest{
SQLTemplate: sqltemplate.New(k.dialect),
sqlKVSection: sqlKVSection{section},
Options: opt,
})
if err != nil {
yield("", err)
return
}
for rows.Next() {
var key string
if err := rows.Scan(&key); err != nil {
yield("", fmt.Errorf("error reading row: %w", err))
return
}
if !yield(strings.TrimPrefix(key, section+"/"), nil) {
return
}
}
if err := rows.Err(); err != nil {
yield("", fmt.Errorf("failed to read rows: %w", err))
}
}
}
+112 -52
View File
@@ -88,8 +88,17 @@ func RunKVTest(t *testing.T, newKV NewKVFunc, opts *KVTestOptions) {
}
}
func prefixKey(nsPrefix, key string) string {
return nsPrefix + "/" + key
func namespacedKeys(nsPrefix string, keys []string) []string {
prefixed := make([]string, 0, len(keys))
for _, k := range keys {
prefixed = append(prefixed, nsPrefix+"/"+k)
}
return prefixed
}
func namespacedKey(nsPrefix, key string) string {
return namespacedKeys(nsPrefix, []string{key})[0]
}
func runTestKVGet(t *testing.T, kv resource.KV, nsPrefix string) {
@@ -97,7 +106,7 @@ func runTestKVGet(t *testing.T, kv resource.KV, nsPrefix string) {
t.Run("get existing key", func(t *testing.T) {
// First save a key
existingKey := prefixKey(nsPrefix, "existing-key")
existingKey := namespacedKey(nsPrefix, "existing-key")
testValue := "test value for get"
saveKVHelper(t, kv, ctx, testSection, existingKey, strings.NewReader(testValue))
@@ -116,13 +125,13 @@ func runTestKVGet(t *testing.T, kv resource.KV, nsPrefix string) {
})
t.Run("get non-existent key", func(t *testing.T) {
_, err := kv.Get(ctx, testSection, prefixKey(nsPrefix, "non-existent-key"))
_, err := kv.Get(ctx, testSection, namespacedKey(nsPrefix, "non-existent-key"))
assert.Error(t, err)
assert.Equal(t, resource.ErrNotFound, err)
})
t.Run("get with empty section", func(t *testing.T) {
_, err := kv.Get(ctx, "", prefixKey(nsPrefix, "some-key"))
_, err := kv.Get(ctx, "", namespacedKey(nsPrefix, "some-key"))
assert.Error(t, err)
assert.Contains(t, err.Error(), "section is required")
})
@@ -215,7 +224,7 @@ func runTestKVDelete(t *testing.T, kv resource.KV, nsPrefix string) {
t.Run("delete existing key", func(t *testing.T) {
// First create a key
deleteKey := prefixKey(nsPrefix, "delete-key")
deleteKey := namespacedKey(nsPrefix, "delete-key")
saveKVHelper(t, kv, ctx, testSection, deleteKey, strings.NewReader("delete me"))
// Verify it exists
@@ -233,13 +242,13 @@ func runTestKVDelete(t *testing.T, kv resource.KV, nsPrefix string) {
})
t.Run("delete non-existent key", func(t *testing.T) {
err := kv.Delete(ctx, testSection, prefixKey(nsPrefix, "non-existent-delete-key"))
err := kv.Delete(ctx, testSection, namespacedKey(nsPrefix, "non-existent-delete-key"))
assert.Error(t, err)
assert.Equal(t, resource.ErrNotFound, err)
})
t.Run("delete with empty section", func(t *testing.T) {
err := kv.Delete(ctx, "", prefixKey(nsPrefix, "some-key"))
err := kv.Delete(ctx, "", namespacedKey(nsPrefix, "some-key"))
assert.Error(t, err)
assert.Contains(t, err.Error(), "section is required")
})
@@ -253,17 +262,16 @@ func runTestKVDelete(t *testing.T, kv resource.KV, nsPrefix string) {
func runTestKVKeys(t *testing.T, kv resource.KV, nsPrefix string) {
ctx := testutil.NewTestContext(t, time.Now().Add(30*time.Second))
section := nsPrefix + "-keys"
// Setup test data
testKeys := []string{"a1", "a2", "b1", "b2", "c1"}
testKeys := namespacedKeys(nsPrefix, []string{"a1", "a2", "b1", "b2", "c1"})
for _, key := range testKeys {
saveKVHelper(t, kv, ctx, section, key, strings.NewReader("value"+key))
saveKVHelper(t, kv, ctx, testSection, key, strings.NewReader("value"+key))
}
t.Run("list all keys", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, section, resource.ListOptions{}) {
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{}) {
require.NoError(t, err)
keys = append(keys, k)
}
@@ -276,134 +284,186 @@ func runTestKVKeys(t *testing.T, kv resource.KV, nsPrefix string) {
for k, err := range kv.Keys(ctx, "", resource.ListOptions{}) {
if err != nil {
errors = append(errors, err)
break
continue
}
keys = append(keys, k)
}
assert.Len(t, errors, 1)
require.Len(t, errors, 1)
assert.Contains(t, errors[0].Error(), "section is required")
assert.Empty(t, keys)
})
t.Run("invalid sort option, defaults to asc", func(t *testing.T) {
var keys []string
var errors []error
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{
Sort: resource.SortOrder(100),
}) {
if err != nil {
errors = append(errors, err)
continue
}
keys = append(keys, k)
}
assert.Empty(t, errors)
assert.Equal(t, testKeys, keys)
})
t.Run("list keys with end key < start key", func(t *testing.T) {
var keys []string
var errors []error
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{
StartKey: namespacedKey(nsPrefix, "c"),
EndKey: namespacedKey(nsPrefix, "a"),
}) {
if err != nil {
errors = append(errors, err)
continue
}
keys = append(keys, k)
}
// Nothing is yielded
assert.Empty(t, errors)
assert.Empty(t, keys)
})
t.Run("list keys returns 0 keys", func(t *testing.T) {
// Use a different section with no keys
emptySection := nsPrefix + "-empty-keys"
// Use a key range with no keys.
startKey, endKey := "aaaaa", "aaaaz"
var keys []string
for k, err := range kv.Keys(ctx, emptySection, resource.ListOptions{}) {
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{
StartKey: startKey,
EndKey: endKey,
}) {
require.NoError(t, err)
keys = append(keys, k)
}
assert.Empty(t, keys)
assert.Len(t, keys, 0)
})
t.Run("interrupting the iterator", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{}) {
require.NoError(t, err)
keys = append(keys, k)
if len(keys) == 2 {
break
}
}
assert.Equal(t, namespacedKeys(nsPrefix, []string{"a1", "a2"}), keys)
})
}
func runTestKVKeysWithLimits(t *testing.T, kv resource.KV, nsPrefix string) {
ctx := testutil.NewTestContext(t, time.Now().Add(30*time.Second))
section := nsPrefix + "-keys-limits"
// Setup test data
testKeys := []string{"a1", "a2", "b1", "b2", "c1", "c2", "d1", "d2"}
testKeys := namespacedKeys(nsPrefix, []string{"a1", "a2", "b1", "b2", "c1", "c2", "d1", "d2"})
for _, key := range testKeys {
saveKVHelper(t, kv, ctx, section, key, strings.NewReader("value"+key))
saveKVHelper(t, kv, ctx, testSection, key, strings.NewReader("value"+key))
}
t.Run("keys with limit", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, section, resource.ListOptions{Limit: 3}) {
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{Limit: 3}) {
require.NoError(t, err)
keys = append(keys, k)
}
assert.Equal(t, []string{"a1", "a2", "b1"}, keys)
assert.Equal(t, namespacedKeys(nsPrefix, []string{"a1", "a2", "b1"}), keys)
})
t.Run("keys with range", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, section, resource.ListOptions{StartKey: "b", EndKey: "d"}) {
require.NoError(t, err)
keys = append(keys, k)
}
assert.Equal(t, []string{"b1", "b2", "c1", "c2"}, keys)
})
t.Run("keys with prefix", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, section, resource.ListOptions{
StartKey: "c",
EndKey: resource.PrefixRangeEnd("c"),
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{
StartKey: namespacedKey(nsPrefix, "b"),
EndKey: namespacedKey(nsPrefix, "d"),
}) {
require.NoError(t, err)
keys = append(keys, k)
}
assert.Equal(t, []string{"c1", "c2"}, keys)
assert.Equal(t, namespacedKeys(nsPrefix, []string{"b1", "b2", "c1", "c2"}), keys)
})
t.Run("keys with prefix", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{
StartKey: namespacedKey(nsPrefix, "c"),
EndKey: namespacedKey(nsPrefix, resource.PrefixRangeEnd("c")),
}) {
require.NoError(t, err)
keys = append(keys, k)
}
assert.Equal(t, namespacedKeys(nsPrefix, []string{"c1", "c2"}), keys)
})
t.Run("keys with limit and range", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, section, resource.ListOptions{
StartKey: "a",
EndKey: "c",
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{
StartKey: namespacedKey(nsPrefix, "a"),
EndKey: namespacedKey(nsPrefix, "c"),
Limit: 2,
}) {
require.NoError(t, err)
keys = append(keys, k)
}
assert.Equal(t, []string{"a1", "a2"}, keys)
assert.Equal(t, namespacedKeys(nsPrefix, []string{"a1", "a2"}), keys)
})
}
func runTestKVKeysWithSort(t *testing.T, kv resource.KV, nsPrefix string) {
ctx := testutil.NewTestContext(t, time.Now().Add(30*time.Second))
section := nsPrefix + "-keys-sort"
// Setup test data
testKeys := []string{"a1", "a2", "b1", "b2", "c1"}
testKeys := namespacedKeys(nsPrefix, []string{"a1", "a2", "b1", "b2", "c1"})
for _, key := range testKeys {
saveKVHelper(t, kv, ctx, section, key, strings.NewReader("value"+key))
saveKVHelper(t, kv, ctx, testSection, key, strings.NewReader("value"+key))
}
t.Run("keys in ascending order (default)", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, section, resource.ListOptions{Sort: resource.SortOrderAsc}) {
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{Sort: resource.SortOrderAsc}) {
require.NoError(t, err)
keys = append(keys, k)
}
assert.Equal(t, []string{"a1", "a2", "b1", "b2", "c1"}, keys)
assert.Equal(t, namespacedKeys(nsPrefix, []string{"a1", "a2", "b1", "b2", "c1"}), keys)
})
t.Run("keys in descending order", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, section, resource.ListOptions{Sort: resource.SortOrderDesc}) {
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{Sort: resource.SortOrderDesc}) {
require.NoError(t, err)
keys = append(keys, k)
}
assert.Equal(t, []string{"c1", "b2", "b1", "a2", "a1"}, keys)
assert.Equal(t, namespacedKeys(nsPrefix, []string{"c1", "b2", "b1", "a2", "a1"}), keys)
})
t.Run("keys descending with prefix", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, section, resource.ListOptions{
StartKey: "a",
EndKey: resource.PrefixRangeEnd("a"),
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{
StartKey: namespacedKey(nsPrefix, "a"),
EndKey: namespacedKey(nsPrefix, resource.PrefixRangeEnd("a")),
Sort: resource.SortOrderDesc,
}) {
require.NoError(t, err)
keys = append(keys, k)
}
assert.Equal(t, []string{"a2", "a1"}, keys)
assert.Equal(t, namespacedKeys(nsPrefix, []string{"a2", "a1"}), keys)
})
t.Run("keys descending with limit", func(t *testing.T) {
var keys []string
for k, err := range kv.Keys(ctx, section, resource.ListOptions{
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{
Sort: resource.SortOrderDesc,
Limit: 3,
}) {
require.NoError(t, err)
keys = append(keys, k)
}
assert.Equal(t, []string{"c1", "b2", "b1"}, keys)
assert.Equal(t, namespacedKeys(nsPrefix, []string{"c1", "b2", "b1"}), keys)
})
}
+5 -8
View File
@@ -47,14 +47,11 @@ func TestSQLKV(t *testing.T) {
}, &KVTestOptions{
NSPrefix: "sql-kv-test",
SkipTests: map[string]bool{
TestKVSave: true,
TestKVKeys: true,
TestKVKeysWithLimits: true,
TestKVKeysWithSort: true,
TestKVConcurrent: true,
TestKVUnixTimestamp: true,
TestKVBatchGet: true,
TestKVBatchDelete: true,
TestKVSave: true,
TestKVConcurrent: true,
TestKVUnixTimestamp: true,
TestKVBatchGet: true,
TestKVBatchDelete: true,
},
})
}
@@ -0,0 +1,129 @@
package templateGroup
import (
"context"
"embed"
"path"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.yaml.in/yaml/v3"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/grafana/grafana/apps/alerting/notifications/pkg/apis/alertingnotifications/v0alpha1"
"github.com/grafana/grafana/pkg/services/featuremgmt"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/tests/api/alerting"
"github.com/grafana/grafana/pkg/tests/apis"
"github.com/grafana/grafana/pkg/tests/apis/alerting/notifications/common"
"github.com/grafana/grafana/pkg/tests/testinfra"
"github.com/grafana/grafana/pkg/util/testutil"
)
//go:embed test-data/*.*
var testData embed.FS
func TestIntegrationImportedTemplates(t *testing.T) {
testutil.SkipIntegrationTestInShortMode(t)
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
EnableFeatureToggles: []string{
featuremgmt.FlagAlertingImportAlertmanagerAPI,
},
})
client := common.NewTemplateGroupClient(t, helper.Org1.Admin)
cliCfg := helper.Org1.Admin.NewRestConfig()
alertingApi := alerting.NewAlertingLegacyAPIClient(helper.GetEnv().Server.HTTPServer.Listener.Addr().String(), cliCfg.Username, cliCfg.Password)
configYaml, err := testData.ReadFile(path.Join("test-data", "imported.yaml"))
require.NoError(t, err)
identifier := "test-create-get-config"
mergeMatchers := "_imported=true"
headers := map[string]string{
"Content-Type": "application/yaml",
"X-Grafana-Alerting-Config-Identifier": identifier,
"X-Grafana-Alerting-Merge-Matchers": mergeMatchers,
}
var amConfig apimodels.AlertmanagerUserConfig
require.NoError(t, yaml.Unmarshal(configYaml, &amConfig))
response := alertingApi.ConvertPrometheusPostAlertmanagerConfig(t, amConfig, headers)
require.Equal(t, "success", response.Status)
templates, err := client.List(context.Background(), metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, templates.Items, 3)
require.Equal(t, v0alpha1.DefaultTemplateTitle, templates.Items[0].Spec.Title)
require.Equal(t, "imported", templates.Items[1].Spec.Title)
require.Equal(t, "template", templates.Items[2].Spec.Title)
t.Run("should be correct kind", func(t *testing.T) {
assert.Equal(t,
v0alpha1.TemplateGroupSpec{
Title: "imported",
Content: amConfig.TemplateFiles["imported"],
Kind: v0alpha1.TemplateGroupTemplateKindMimir,
}, templates.Items[1].Spec)
assert.Equal(t,
v0alpha1.TemplateGroupSpec{
Title: "template",
Content: amConfig.TemplateFiles["template"],
Kind: v0alpha1.TemplateGroupTemplateKindMimir,
}, templates.Items[2].Spec)
})
t.Run("should be provisioned", func(t *testing.T) {
for _, tpl := range templates.Items[1:] {
assert.EqualValues(t, models.ProvenanceConvertedPrometheus, tpl.GetProvenanceStatus())
}
})
t.Run("should not be able to update", func(t *testing.T) {
tpl := templates.Items[1]
tpl.Spec.Content = "new content"
_, err := client.Update(context.Background(), &tpl, metav1.UpdateOptions{})
require.Truef(t, errors.IsBadRequest(err), "expected bad request but got %s", err)
})
t.Run("should not be able to delete", func(t *testing.T) {
err := client.Delete(context.Background(), templates.Items[1].Name, metav1.DeleteOptions{})
require.Truef(t, errors.IsBadRequest(err), "expected bad request but got %s", err)
})
t.Run("should not conflict with Grafana kind", func(t *testing.T) {
tpl := v0alpha1.TemplateGroup{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
},
Spec: templates.Items[1].Spec,
}
tpl.Spec.Kind = v0alpha1.TemplateGroupTemplateKindGrafana
created, err := client.Create(context.Background(), &tpl, metav1.CreateOptions{})
require.NoError(t, err)
assert.NotEqual(t, templates.Items[1].Name, created.Name)
})
t.Run("sort by kind and then name", func(t *testing.T) {
templates, err := client.List(context.Background(), metav1.ListOptions{})
require.NoError(t, err)
require.Len(t, templates.Items, 4)
assert.Equal(t, v0alpha1.DefaultTemplateTitle, templates.Items[0].Spec.Title)
assert.Equal(t, "imported", templates.Items[1].Spec.Title)
assert.Equal(t, v0alpha1.TemplateGroupTemplateKindGrafana, templates.Items[1].Spec.Kind)
assert.Equal(t, "imported", templates.Items[2].Spec.Title)
assert.Equal(t, v0alpha1.TemplateGroupTemplateKindMimir, templates.Items[2].Spec.Kind)
assert.Equal(t, "template", templates.Items[3].Spec.Title)
})
}
@@ -54,6 +54,7 @@ func TestIntegrationResourceIdentifier(t *testing.T) {
Spec: v0alpha1.TemplateGroupSpec{
Title: "templateGroup",
Content: `{{ define "test" }} test {{ end }}`,
Kind: v0alpha1.TemplateGroupTemplateKindGrafana,
},
}
@@ -112,6 +113,7 @@ func TestIntegrationResourceIdentifier(t *testing.T) {
require.Equal(t, v0alpha1.TemplateGroupSpec{
Title: v0alpha1.DefaultTemplateTitle,
Content: defaultDefn.Template,
Kind: v0alpha1.TemplateGroupTemplateKindGrafana,
}, actual.Spec)
defaultTemplateGroup = actual
})
@@ -226,6 +228,7 @@ func TestIntegrationAccessControl(t *testing.T) {
Spec: v0alpha1.TemplateGroupSpec{
Title: fmt.Sprintf("template-group-1-%s", tc.user.Identity.GetLogin()),
Content: `{{ define "test" }} test {{ end }}`,
Kind: v0alpha1.TemplateGroupTemplateKindGrafana,
},
}
expected.SetProvenanceStatus("")
@@ -385,6 +388,7 @@ func TestIntegrationProvisioning(t *testing.T) {
Spec: v0alpha1.TemplateGroupSpec{
Title: "template-group-1",
Content: `{{ define "test" }} test {{ end }}`,
Kind: v0alpha1.TemplateGroupTemplateKindGrafana,
},
}, v1.CreateOptions{})
require.NoError(t, err)
@@ -428,6 +432,7 @@ func TestIntegrationOptimisticConcurrency(t *testing.T) {
Spec: v0alpha1.TemplateGroupSpec{
Title: "template-group-1",
Content: `{{ define "test" }} test {{ end }}`,
Kind: v0alpha1.TemplateGroupTemplateKindGrafana,
},
}
@@ -510,6 +515,7 @@ func TestIntegrationPatch(t *testing.T) {
Spec: v0alpha1.TemplateGroupSpec{
Title: "template-group",
Content: `{{ define "test" }} test {{ end }}`,
Kind: v0alpha1.TemplateGroupTemplateKindGrafana,
},
}
@@ -568,6 +574,7 @@ func TestIntegrationListSelector(t *testing.T) {
Spec: v0alpha1.TemplateGroupSpec{
Title: "test1",
Content: `{{ define "test1" }} test {{ end }}`,
Kind: v0alpha1.TemplateGroupTemplateKindGrafana,
},
}
template1, err := adminClient.Create(ctx, template1, v1.CreateOptions{})
@@ -580,6 +587,7 @@ func TestIntegrationListSelector(t *testing.T) {
Spec: v0alpha1.TemplateGroupSpec{
Title: "test2",
Content: `{{ define "test2" }} test {{ end }}`,
Kind: v0alpha1.TemplateGroupTemplateKindGrafana,
},
}
template2, err = adminClient.Create(ctx, template2, v1.CreateOptions{})
@@ -655,3 +663,37 @@ func TestIntegrationListSelector(t *testing.T) {
require.NotEqualf(t, templates.DefaultTemplateName, list.Items[1].Name, "Expected non-default template but got %s", list.Items[1].Name)
})
}
func TestIntegrationKinds(t *testing.T) {
testutil.SkipIntegrationTestInShortMode(t)
ctx := context.Background()
helper := getTestHelper(t)
client := common.NewTemplateGroupClient(t, helper.Org1.Admin)
newTemplate := &v0alpha1.TemplateGroup{
ObjectMeta: v1.ObjectMeta{
Namespace: "default",
},
Spec: v0alpha1.TemplateGroupSpec{
Title: "templateGroup",
Content: `{{ define "test" }} test {{ end }}`,
Kind: v0alpha1.TemplateGroupTemplateKindMimir,
},
}
t.Run("should not let create Mimir template", func(t *testing.T) {
_, err := client.Create(ctx, newTemplate, v1.CreateOptions{})
require.Truef(t, errors.IsBadRequest(err), "expected bad request but got %s", err)
})
t.Run("should not let change kind", func(t *testing.T) {
newTemplate.Spec.Kind = v0alpha1.TemplateGroupTemplateKindGrafana
created, err := client.Create(ctx, newTemplate, v1.CreateOptions{})
require.NoError(t, err)
created.Spec.Kind = v0alpha1.TemplateGroupTemplateKindMimir
_, err = client.Update(ctx, created, v1.UpdateOptions{})
require.Truef(t, errors.IsBadRequest(err), "expected bad request but got %s", err)
})
}
@@ -0,0 +1,15 @@
alertmanager_config: |
route:
receiver: sinkhole
group_by:
- alertname
receivers:
- name: sinkhole
template_files:
imported: |
{{ define "imported" }}
{{ end }}
template: |
{{ define "template" }}
{{ template "imported" . }}
{{ end }}
@@ -3783,18 +3783,29 @@
"type": "object",
"required": [
"title",
"content"
"content",
"kind"
],
"properties": {
"content": {
"type": "string"
},
"kind": {
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.alerting.notifications.pkg.apis.alertingnotifications.v0alpha1.TemplateGroupTemplateKind"
},
"title": {
"type": "string"
}
},
"additionalProperties": false
},
"com.github.grafana.grafana.apps.alerting.notifications.pkg.apis.alertingnotifications.v0alpha1.TemplateGroupTemplateKind": {
"type": "string",
"enum": [
"grafana",
"mimir"
]
},
"com.github.grafana.grafana.apps.alerting.notifications.pkg.apis.alertingnotifications.v0alpha1.TimeInterval": {
"type": "object",
"required": [
@@ -56,6 +56,7 @@ describe('EmptyTransformationsMessage', () => {
onShowPicker={onShowPicker}
onGoToQueries={onGoToQueries}
onAddTransformation={onAddTransformation}
data={[]}
/>
);
@@ -75,6 +76,7 @@ describe('EmptyTransformationsMessage', () => {
onShowPicker={onShowPicker}
onGoToQueries={onGoToQueries}
onAddTransformation={onAddTransformation}
data={[]}
/>
);
@@ -92,6 +94,7 @@ describe('EmptyTransformationsMessage', () => {
onShowPicker={onShowPicker}
onGoToQueries={onGoToQueries}
onAddTransformation={onAddTransformation}
data={[]}
/>
);
@@ -103,13 +106,15 @@ describe('EmptyTransformationsMessage', () => {
});
it('should not show SQL transformation card when onGoToQueries is not provided', () => {
render(<EmptyTransformationsMessage onShowPicker={onShowPicker} onAddTransformation={onAddTransformation} />);
render(
<EmptyTransformationsMessage onShowPicker={onShowPicker} onAddTransformation={onAddTransformation} data={[]} />
);
expect(screen.queryByText('Transform with SQL')).not.toBeInTheDocument();
});
it('should not show transformation cards grid when neither onGoToQueries nor onAddTransformation are provided', () => {
render(<EmptyTransformationsMessage onShowPicker={onShowPicker} />);
render(<EmptyTransformationsMessage onShowPicker={onShowPicker} data={[]} />);
expect(screen.queryByText('Transform with SQL')).not.toBeInTheDocument();
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { DataTransformerID, standardTransformersRegistry, TransformerRegistryItem } from '@grafana/data';
import { DataFrame, DataTransformerID, standardTransformersRegistry, TransformerRegistryItem } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { t, Trans } from '@grafana/i18n';
import { reportInteraction } from '@grafana/runtime';
@@ -16,6 +16,7 @@ interface EmptyTransformationsProps {
onShowPicker: () => void;
onGoToQueries?: () => void;
onAddTransformation?: (transformationId: string) => void;
data: DataFrame[];
}
const TRANSFORMATION_IDS = [
@@ -121,6 +122,7 @@ export function NewEmptyTransformationsMessage(props: EmptyTransformationsProps)
showIllustrations={true}
showPluginState={false}
showTags={false}
data={props.data}
/>
))}
</Grid>
@@ -152,6 +152,7 @@ export function PanelDataTransformationsTabRendered({ model }: SceneComponentPro
onShowPicker={openDrawer}
onGoToQueries={onGoToQueries}
onAddTransformation={onAddTransformation}
data={sourceData.data.series}
/>
{transformationsDrawer}
</>
@@ -554,7 +554,7 @@ export function sceneVariablesSetToSchemaV2Variables(
...validateFiltersOrigin(variable.state.originFilters),
...validateFiltersOrigin(variable.state.filters),
],
defaultKeys: variable.state.defaultKeys || [], //FIXME what is the default value?
defaultKeys: variable.state.defaultKeys || [],
allowCustomValue: variable.state.allowCustomValue ?? true,
},
};
@@ -1137,7 +1137,13 @@
"datasource": { "name": "esmce00tbim8" },
"group": "opensearch",
"spec": {
"allowCustomValue": true
"name": "adhocVar",
"allowCustomValue": true,
"filters": [],
"baseFilters": [],
"defaultKeys": [],
"hide": "dontHide",
"skipUrlSync": false
}
},
{
@@ -365,6 +365,35 @@ describe('transformSaveModelSchemaV2ToScene', () => {
expect(getQueryRunnerFor(vizPanels[0])?.state.datasource?.uid).toBe(MIXED_DATASOURCE_NAME);
});
describe('adhoc variables', () => {
it('should convert empty defaultKeys array to undefined', () => {
const dashboard = cloneDeep(defaultDashboard);
const adhocVar = dashboard.spec.variables.find((v) => v.kind === 'AdhocVariable') as AdhocVariableKind;
adhocVar.spec.defaultKeys = [];
const scene = transformSaveModelSchemaV2ToScene(dashboard);
const adhocVariable = scene.state.$variables?.getByName('adhocVar') as AdHocFiltersVariable;
expect(adhocVariable).toBeInstanceOf(AdHocFiltersVariable);
expect(adhocVariable.state.defaultKeys).toBeUndefined();
});
it('should preserve non-empty defaultKeys array', () => {
const dashboard = cloneDeep(defaultDashboard);
const adhocVar = dashboard.spec.variables.find((v) => v.kind === 'AdhocVariable') as AdhocVariableKind;
expect(adhocVar.spec.defaultKeys.length).toBeGreaterThan(0);
const scene = transformSaveModelSchemaV2ToScene(dashboard);
const adhocVariable = scene.state.$variables?.getByName('adhocVar') as AdHocFiltersVariable;
expect(adhocVariable).toBeInstanceOf(AdHocFiltersVariable);
expect(adhocVariable.state.defaultKeys).toEqual(adhocVar.spec.defaultKeys);
});
});
describe('When creating a snapshot dashboard scene', () => {
it('should initialize a dashboard scene with SnapshotVariables', () => {
const snapshot: DashboardWithAccessInfo<DashboardV2Spec> = {
@@ -401,6 +430,29 @@ describe('transformSaveModelSchemaV2ToScene', () => {
expect(intervalSnapshot.state.text).toBe('1m');
expect(intervalSnapshot.state.isReadOnly).toBe(true);
});
it('should convert empty defaultKeys array to undefined for adhoc variables', () => {
const snapshot: DashboardWithAccessInfo<DashboardV2Spec> = cloneDeep({
...defaultDashboard,
metadata: {
...defaultDashboard.metadata,
annotations: {
...defaultDashboard.metadata.annotations,
[AnnoKeyDashboardIsSnapshot]: 'true',
},
},
});
const adhocVar = snapshot.spec.variables.find((v) => v.kind === 'AdhocVariable') as AdhocVariableKind;
adhocVar.spec.defaultKeys = [];
const scene = transformSaveModelSchemaV2ToScene(snapshot);
const adhocVariable = scene.state.$variables?.getByName('adhocVar') as AdHocFiltersVariable;
expect(adhocVariable).toBeInstanceOf(AdHocFiltersVariable);
expect(adhocVariable.state.defaultKeys).toBeUndefined();
});
});
describe('meta', () => {
@@ -329,7 +329,7 @@ function createSceneVariableFromVariableModel(variable: TypedVariableModelV2): S
applyMode: 'auto',
filters: variable.spec.filters ?? [],
baseFilters: variable.spec.baseFilters ?? [],
defaultKeys: variable.spec.defaultKeys,
defaultKeys: variable.spec.defaultKeys.length ? variable.spec.defaultKeys : undefined,
useQueriesAsFilterForOptions: true,
drilldownRecommendationsEnabled: config.featureToggles.drilldownRecommendations,
layout: config.featureToggles.newFiltersUI ? 'combobox' : undefined,
@@ -538,7 +538,7 @@ export function createVariablesForSnapshot(dashboard: DashboardV2Spec): SceneVar
applyMode: 'auto',
filters: v.spec.filters ?? [],
baseFilters: v.spec.baseFilters ?? [],
defaultKeys: v.spec.defaultKeys,
defaultKeys: v.spec.defaultKeys?.length ? v.spec.defaultKeys : undefined,
useQueriesAsFilterForOptions: true,
layout: config.featureToggles.newFiltersUI ? 'combobox' : undefined,
supportsMultiValueOperators: Boolean(
@@ -256,7 +256,8 @@ class UnThemedTransformationsEditor extends React.PureComponent<TransformationsE
onShowPicker={() => {
this.setState({ showPicker: true });
}}
></EmptyTransformationsMessage>
data={this.state.data.series}
/>
);
};
+7
View File
@@ -0,0 +1,7 @@
# WARNING: Do NOT modify the contents of this directory!
User modifications in this directory to change the runtime behavior or appearance of Grafana are NOT supported. Doing so could result in undefined behaviour including: bugs, security vulnerabilities, or critical failures.
Grafana is an open source project. Please feel free to contribute changes for review if you have specific bug fix or feature that you think might be useful to the rest of the community.
- SEE: https://github.com/grafana/plugin-tools/blob/main/CONTRIBUTING.md
+11 -19
View File
@@ -3351,6 +3351,7 @@
"login-attribute-path-label": "Cesta atributu přihlášení",
"login-prompt-consent": "Souhlas",
"login-prompt-description": "Označuje typ interakce uživatele při přihlášení pomocí IdP.",
"login-prompt-description-google": "",
"login-prompt-label": "Výzva k přihlášení",
"login-prompt-login": "Přihlášení",
"login-prompt-select-account": "Vyberte účet",
@@ -3408,6 +3409,7 @@
"use-pkce-description": "Pokud je povoleno, Grafana použije <2>ověřovací klíč pro výměnu kódů (PKCE)</2> s udělením autorizačního kódu OAuth2.",
"use-pkce-label": "Použít PKCE",
"use-refresh-token-description": "Pokud je povoleno, Grafana načte nový přístupový token pomocí obnovovacího tokenu poskytnutého poskytovatelem OAuth2.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Použít token pro obnovení",
"validate-hosted-domain-description": "Pokud je povoleno, Grafana porovná hostovanou doménu načtenou z tokenu Google ID se seznamem „{{ allowedDomainsLabel }}“ zadaným uživatelem.",
"validate-hosted-domain-label": "Ověřit hostovanou doménu",
@@ -10267,7 +10269,8 @@
"copy": "Kopírovat výběr",
"disable-menu": "Zakázat nabídku",
"line-contains": "Přidat jako řádek obsahuje filtr",
"line-contains-not": "Přidat jako řádek neobsahuje filtr"
"line-contains-not": "Přidat jako řádek neobsahuje filtr",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Rozlišení časového razítka",
@@ -11973,7 +11976,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Když připojíte celou instanci, nástěnky nebudou během migrace k dispozici. Doporučujeme uživatele před zahájením procesu varovat.",
"modal-description-public-access": "Nastavte veřejný přístup k vaší instanci Grafana pro povolení integrace GitHubu",
"modal-description-required-features": "Povolit požadované funkce Grafany pro zajištění",
"modal-title-set-up-public-access": "Nastavit veřejný přístup",
@@ -11985,8 +11987,7 @@
"step-title-copy-url": "Kopírovat veřejnou adresu URL",
"step-title-enable-feature-toggles": "Povolit požadované přepínače funkcí",
"step-title-start-ngrok": "Spustit „ngrok“ pro dočasný veřejný přístup",
"step-title-update-grafana-config": "Aktualizujte konfiguraci Grafany",
"title-setting-connection-could-cause-temporary-outage": "Nastavení tohoto připojení může způsobit dočasný výpadek"
"step-title-update-grafana-config": "Aktualizujte konfiguraci Grafany"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Zobrazujte a spravujte vazby zajištění"
@@ -12055,18 +12056,15 @@
},
"home-page": {
"button-delete-repositories": "Odstranit úložiště",
"configured-repositories-while-running-legacy-storage": "Konfigurovaná úložiště nebudou fungovat při spuštění starší verze úložiště.",
"confirm-delete-repositories": "Opravdu chcete odstranit všechna nakonfigurovaná úložiště? Tato akce je nevratná.",
"error-delete-all-repositories": "Nepodařilo se odstranit všechna úložiště",
"remove-all-configured-repositories": "Odebrat všechna nakonfigurovaná úložiště",
"subtitle": "Zobrazujte a spravujte nakonfigurovaná úložiště",
"success-all-repositories-deleted": "Všechna nakonfigurovaná úložiště byla odstraněna",
"tab-getting-started": "Začínáme",
"tab-getting-started-title": "Začínáme",
"tab-repositories": "Úložiště",
"tab-repositories-title": "Seznam úložišť",
"title-delete-all-configured-repositories": "Odstranit všechna nakonfigurovaná úložiště",
"title-legacy-storage-detected": "Bylo zjištěno starší úložiště"
"title-delete-all-configured-repositories": "Odstranit všechna nakonfigurovaná úložiště"
},
"http-utils": {
"access-denied": "",
@@ -12106,7 +12104,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12186,7 +12183,6 @@
"repository-status-page": {
"back-to-repositories": "Zpět na úložiště",
"cleaning-up-resources": "Čištění zdrojů úložiště",
"legacy-storage-message": "Instance nespustila sjednocené úložiště – vyžaduje průvodce migrací",
"not-found": "nenalezeno",
"not-found-message": "Úložiště nebylo nalezeno",
"repository-config-exists-configuration": "Ujistěte se, že konfigurace úložiště existuje v konfiguračním souboru.",
@@ -12195,7 +12191,6 @@
"tab-resources": "Zdroje",
"tab-resources-title": "",
"title": "Stav úložiště",
"title-legacy-storage": "Starší verze úložiště",
"title-queued-for-deletion": "Zařazeno do fronty k odstranění"
},
"repository-type-cards": {
@@ -12291,9 +12286,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Zahrnout předložení pro každou historickou hodnotu",
"synchronization-options": "Možnosti synchronizace"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "a klikněte",
@@ -12310,11 +12303,11 @@
},
"warning-title-default": "Varování",
"wizard": {
"alert-point-1": "Během tohoto procesu nebude možné vytvářet, upravovat ani odstraňovat zdroje. V posledním kroku zmizí.",
"alert-point-2": "Po zajištění se zdroje znovu zobrazí a budou spravovány prostřednictvím externího úložiště.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Doba trvání tohoto procesu závisí na počtu zapojených zdrojů.",
"alert-point-4": "Správci podnikových instancí mohou uživatelům zobrazit banner oznámení. Podrobné pokyny najdete v <2>této příručce</2>.",
"alert-title": "Důležité: Nebudou ztracena žádná data ani konfigurace, ale nástěnky budou na několik minut dočasně nedostupné.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Dokončit",
@@ -12332,7 +12325,6 @@
"step-finish": "Vybrat další nastavení",
"step-synchronize": "Synchronizovat s externím úložištěm",
"sync-description": "Synchronizujte zdroje s externím úložištěm. Po tomto jednorázovém kroku budou všechny budoucí aktualizace automaticky uloženy do úložiště a zajištěny zpět do instance.",
"sync-option-history": "Historie",
"title-bootstrap": "Vyberte, co chcete synchronizovat",
"title-connect": "Připojit k externímu úložišti",
"title-finish": "Vybrat další nastavení",
+11 -19
View File
@@ -3329,6 +3329,7 @@
"login-attribute-path-label": "Anmeldeattributpfad",
"login-prompt-consent": "Einverständnis",
"login-prompt-description": "Gibt die Art der Nutzerinteraktion an, wenn sich der Nutzer mit dem IdP anmeldet.",
"login-prompt-description-google": "",
"login-prompt-label": "Anmeldeaufforderung",
"login-prompt-login": "Anmelden",
"login-prompt-select-account": "Account auswählen",
@@ -3386,6 +3387,7 @@
"use-pkce-description": "Wenn dies aktiviert ist, nutzt Grafana <2>Proof Key for Code Exchange (PKCE)</2> mit OAuth2 Authorization Code Grant.",
"use-pkce-label": "PKCE verwenden",
"use-refresh-token-description": "Wenn dies aktiviert ist, ruft Grafana einen neuen Zugriffstoken mit dem vom OAuth2-Anbieter bereitgestellten Aktualisierungstoken ab.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Aktualisierungstoken nutzen",
"validate-hosted-domain-description": "Wenn dies aktiviert ist, wird Grafana die vom Google-ID-Token abgerufene gehostete Domain mit der vom Nutzer spezifizierten Liste „{{ allowedDomainsLabel }}“ abgleichen.",
"validate-hosted-domain-label": "Gehostete Domain validieren",
@@ -10181,7 +10183,8 @@
"copy": "Auswahl kopieren",
"disable-menu": "Menü deaktivieren",
"line-contains": "„Als Zeile hinzufügen“ enthält Filter",
"line-contains-not": "„Als Zeile hinzufügen“ enthält keinen Filter"
"line-contains-not": "„Als Zeile hinzufügen“ enthält keinen Filter",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Zeitstempelauflösung",
@@ -11871,7 +11874,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Wenn Sie Ihre gesamte Instanz verbinden, sind während der Ausführung der Migration keine Dashboards verfügbar. Wir empfehlen Ihnen, Ihre Nutzer vor Beginn des Prozesses zu benachrichtigen.",
"modal-description-public-access": "Richten Sie zur Aktivierung der GitHub-Integration den öffentlichen Zugriff auf Ihre Grafana-Instanz ein",
"modal-description-required-features": "Benötigte Grafana-Funktionen für die Bereitstellung aktivieren",
"modal-title-set-up-public-access": "Öffentlichen Zugriff einrichten",
@@ -11883,8 +11885,7 @@
"step-title-copy-url": "Kopieren Sie Ihre öffentliche URL",
"step-title-enable-feature-toggles": "Benötigte Funktionsschalter aktivieren",
"step-title-start-ngrok": "ngrok für temporären öffentlichen Zugriff starten",
"step-title-update-grafana-config": "Aktualisieren Sie Ihre Grafana-Konfiguration",
"title-setting-connection-could-cause-temporary-outage": "Das Einrichten dieser Verbindung kann zu einem vorübergehenden Ausfall führen"
"step-title-update-grafana-config": "Aktualisieren Sie Ihre Grafana-Konfiguration"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Sehen und verwalten Sie Ihre Bereitstellungsverbindungen"
@@ -11953,18 +11954,15 @@
},
"home-page": {
"button-delete-repositories": "Repositorys löschen",
"configured-repositories-while-running-legacy-storage": "Konfigurierte Repositorys funktionieren nicht, wenn veralteter Speicher ausgeführt wird.",
"confirm-delete-repositories": "Sind Sie sicher, dass Sie alle konfigurierten Repositorys löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
"error-delete-all-repositories": "Es konnten nicht alle Repositorys gelöscht werden",
"remove-all-configured-repositories": "Alle konfigurierten Repositorys entfernen",
"subtitle": "Sehen und verwalten Sie Ihre konfigurierten Repositorys",
"success-all-repositories-deleted": "Alle konfigurierten Repositorys gelöscht",
"tab-getting-started": "Erste Schritte",
"tab-getting-started-title": "Erste Schritte",
"tab-repositories": "Repositorys",
"tab-repositories-title": "Liste der Repositorys",
"title-delete-all-configured-repositories": "Alle konfigurierten Repositorys löschen",
"title-legacy-storage-detected": "Veralteter Speicher erkannt"
"title-delete-all-configured-repositories": "Alle konfigurierten Repositorys löschen"
},
"http-utils": {
"access-denied": "",
@@ -12004,7 +12002,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12084,7 +12081,6 @@
"repository-status-page": {
"back-to-repositories": "Zurück zu den Repositorys",
"cleaning-up-resources": "Bereinigen von Repository-Ressourcen",
"legacy-storage-message": "Die Instanz führt noch keinen Unified Storage aus – Migrationsassistent erforderlich",
"not-found": "nicht gefunden",
"not-found-message": "Repository nicht gefunden",
"repository-config-exists-configuration": "Achten Sie darauf, dass die Repository-config in der Konfigurationsdatei vorhanden ist.",
@@ -12093,7 +12089,6 @@
"tab-resources": "Ressourcen",
"tab-resources-title": "",
"title": "Repository-Status",
"title-legacy-storage": "Veralteter Speicher",
"title-queued-for-deletion": "Zum Löschen in die Warteschlange gestellt"
},
"repository-type-cards": {
@@ -12189,9 +12184,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Commits für jeden historischen Wert erfassen",
"synchronization-options": "Synchronisierungsoptionen"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "und klicken",
@@ -12208,11 +12201,11 @@
},
"warning-title-default": "Warnung",
"wizard": {
"alert-point-1": "Während dieses Vorgangs können keine Ressourcen erstellt, bearbeitet oder gelöscht werden. Im letzten Schritt werden sie entfernt.",
"alert-point-2": "Sobald die Bereitstellung abgeschlossen ist, werden die Ressourcen wieder angezeigt und über einen externen Speicher verwaltet.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Die Dauer dieses Prozesses hängt von der Anzahl der betroffenen Ressourcen ab.",
"alert-point-4": "Administratoren von Enterprise-Instanzen können Nutzern gegenüber ein Ankündigungsbanner anzeigen. Schritt-für-Schritt-Anweisungen finden Sie in <2>dieser Anleitung</2>.",
"alert-title": "Wichtig: Es gehen keine Daten oder Konfigurationen verloren, aber die Dashboards sind für einige Minuten vorübergehend nicht verfügbar.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Fertigstellen",
@@ -12230,7 +12223,6 @@
"step-finish": "Zusätzliche Einstellungen auswählen",
"step-synchronize": "Mit externem Speicher synchronisieren",
"sync-description": "Synchronisieren Sie Ressourcen mit externem Speicher. Nach diesem einmaligen Schritt werden alle zukünftigen Updates automatisch im Repository gespeichert und wieder in der Instanz bereitgestellt.",
"sync-option-history": "Verlauf",
"title-bootstrap": "Wählen Sie aus, was synchronisiert wird",
"title-connect": "Mit externem Speicher verbinden",
"title-finish": "Zusätzliche Einstellungen auswählen",
+11 -19
View File
@@ -3329,6 +3329,7 @@
"login-attribute-path-label": "Ruta del atributo de inicio de sesión",
"login-prompt-consent": "Aceptar",
"login-prompt-description": "Indica el tipo de interacción del usuario cuando este inicia sesión con el IdP.",
"login-prompt-description-google": "",
"login-prompt-label": "Aviso de inicio de sesión",
"login-prompt-login": "Iniciar sesión",
"login-prompt-select-account": "Seleccionar cuenta",
@@ -3386,6 +3387,7 @@
"use-pkce-description": "Si está habilitado, Grafana utilizará la <2>clave de prueba para el intercambio de código (PKCE)</2> con la concesión de código de autorización OAuth2.",
"use-pkce-label": "Utilizar PKCE",
"use-refresh-token-description": "Si se ha habilitado, Grafana obtendrá un nuevo token de acceso a través del token de actualización que proporcione el proveedor de OAuth2.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Usar token de actualización",
"validate-hosted-domain-description": "Si se ha habilitado, Grafana comparará el dominio alojado recuperado del token de ID de Google con la lista «{{ allowedDomainsLabel }}» que especifique el usuario.",
"validate-hosted-domain-label": "Validar dominio alojado",
@@ -10181,7 +10183,8 @@
"copy": "Copiar selección",
"disable-menu": "Desactivar menú",
"line-contains": "Añadir como filtro «la línea contiene»",
"line-contains-not": "Añadir como filtro «la línea no contiene»"
"line-contains-not": "Añadir como filtro «la línea no contiene»",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Resolución de marca de tiempo",
@@ -11871,7 +11874,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Cuando conectes toda tu instancia, los paneles no estarán disponibles mientras se ejecuta la migración. Recomendamos advertir a tus usuarios antes de iniciar el proceso.",
"modal-description-public-access": "Configura el acceso público a tu instancia de Grafana para habilitar la integración de GitHub",
"modal-description-required-features": "Habilitar las funciones de Grafana necesarias para el aprovisionamiento",
"modal-title-set-up-public-access": "Configurar el acceso público",
@@ -11883,8 +11885,7 @@
"step-title-copy-url": "Copia tu URL pública",
"step-title-enable-feature-toggles": "Habilitar las opciones de las funciones necesarias",
"step-title-start-ngrok": "Iniciar ngrok para el acceso público temporal",
"step-title-update-grafana-config": "Actualiza tu configuración de Grafana",
"title-setting-connection-could-cause-temporary-outage": "Configurar esta conexión podría causar una interrupción temporal"
"step-title-update-grafana-config": "Actualiza tu configuración de Grafana"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Ver y gestionar tus conexiones de aprovisionamiento"
@@ -11953,18 +11954,15 @@
},
"home-page": {
"button-delete-repositories": "Eliminar repositorios",
"configured-repositories-while-running-legacy-storage": "Los repositorios configurados no funcionarán mientras se ejecuta el almacenamiento heredado.",
"confirm-delete-repositories": "¿Seguro que quieres eliminar todos los repositorios configurados? Esta acción no se puede deshacer.",
"error-delete-all-repositories": "Error al eliminar todos los repositorios",
"remove-all-configured-repositories": "Eliminar todos los repositorios configurados",
"subtitle": "Ver y gestionar tus repositorios configurados",
"success-all-repositories-deleted": "Se han eliminado todos los repositorios configurados",
"tab-getting-started": "Cómo empezar",
"tab-getting-started-title": "Cómo empezar",
"tab-repositories": "Repositorios",
"tab-repositories-title": "Lista de repositorios",
"title-delete-all-configured-repositories": "Eliminar todos los repositorios configurados",
"title-legacy-storage-detected": "Almacenamiento heredado detectado"
"title-delete-all-configured-repositories": "Eliminar todos los repositorios configurados"
},
"http-utils": {
"access-denied": "",
@@ -12004,7 +12002,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12084,7 +12081,6 @@
"repository-status-page": {
"back-to-repositories": "Volver a los repositorios",
"cleaning-up-resources": "Limpiando los recursos del repositorio",
"legacy-storage-message": "La instancia aún no está ejecutando almacenamiento unificado, requiere un asistente de migración",
"not-found": "no ha sido encontrado",
"not-found-message": "Repositorio no encontrado",
"repository-config-exists-configuration": "Asegúrate de que la configuración del repositorio exista en el archivo de configuración.",
@@ -12093,7 +12089,6 @@
"tab-resources": "Recursos",
"tab-resources-title": "",
"title": "Estado del repositorio",
"title-legacy-storage": "Almacenamiento heredado",
"title-queued-for-deletion": "En cola para su eliminación"
},
"repository-type-cards": {
@@ -12189,9 +12184,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Incluir confirmaciones para cada valor histórico",
"synchronization-options": "Opciones de sincronización"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "y haz clic en",
@@ -12208,11 +12201,11 @@
},
"warning-title-default": "Advertencia",
"wizard": {
"alert-point-1": "Los recursos no se podrán crear, editar ni eliminar durante este proceso. En el último paso, desaparecerán.",
"alert-point-2": "Una vez que se complete el aprovisionamiento, los recursos volverán a aparecer y se gestionarán a través de un almacenamiento externo.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "La duración de este proceso depende del número de recursos involucrados.",
"alert-point-4": "Los administradores de instancias empresariales pueden mostrar un báner de anuncio a los usuarios. Consulta <2>esta guía</2> para obtener instrucciones paso a paso.",
"alert-title": "Importante: No se perderán datos ni configuraciones, pero los dashboards no estarán disponibles temporalmente durante unos minutos.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Terminar",
@@ -12230,7 +12223,6 @@
"step-finish": "Elegir ajustes adicionales",
"step-synchronize": "Sincronizar con un almacenamiento externo",
"sync-description": "Sincroniza los recursos con un almacenamiento externo. Después de este paso único, todas las actualizaciones futuras se guardarán automáticamente en el repositorio y se aprovisionarán de nuevo en la instancia.",
"sync-option-history": "Historial",
"title-bootstrap": "Elegir qué sincronizar",
"title-connect": "Conectar a un almacenamiento externo",
"title-finish": "Elegir ajustes adicionales",
+11 -19
View File
@@ -3329,6 +3329,7 @@
"login-attribute-path-label": "Chemin d’attribut de connexion",
"login-prompt-consent": "Consentement",
"login-prompt-description": "Indique le type d’interaction utilisateur lors de la connexion via le fournisseur d’identité (IdP).",
"login-prompt-description-google": "",
"login-prompt-label": "Invite de connexion",
"login-prompt-login": "Connexion",
"login-prompt-select-account": "Sélectionner un compte",
@@ -3386,6 +3387,7 @@
"use-pkce-description": "Si cette option est activée, Grafana utilisera la <2>clé de preuve pour l’échange de code (PKCE)</2> avec l’octroi de code d’autorisation OAuth2.",
"use-pkce-label": "Utiliser PKCE",
"use-refresh-token-description": "Si cette option est activée, Grafana récupérera un nouveau jeton d’accès à l’aide du jeton d’actualisation fourni par le fournisseur OAuth2.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Utiliser le jeton d’actualisation",
"validate-hosted-domain-description": "Si cette option est activée, Grafana fera correspondre le domaine hébergé récupéré à partir du jeton d’identification Google à la liste « {{ allowedDomainsLabel }} » spécifiée par l’utilisateur.",
"validate-hosted-domain-label": "Valider le domaine hébergé",
@@ -10181,7 +10183,8 @@
"copy": "Copier la sélection",
"disable-menu": "Désactiver le menu",
"line-contains": "Ajouter car la ligne contient un filtre",
"line-contains-not": "Ajouter car la ligne ne contient pas de filtre"
"line-contains-not": "Ajouter car la ligne ne contient pas de filtre",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Résolution des horodatages",
@@ -11871,7 +11874,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Lorsque vous connectez l’ensemble de votre instance, les tableaux de bord ne seront pas disponibles pendant l’exécution de la migration. Nous vous recommandons d’avertir vos utilisateurs avant de commencer le processus.",
"modal-description-public-access": "Configurez l’accès public à votre instance Grafana pour activer l’intégration GitHub",
"modal-description-required-features": "Activez les fonctionnalités Grafana requises pour la mise en service",
"modal-title-set-up-public-access": "Configurer l’accès public",
@@ -11883,8 +11885,7 @@
"step-title-copy-url": "Copier l’URL publique",
"step-title-enable-feature-toggles": "Activer les bascules de fonctionnalité requises",
"step-title-start-ngrok": "Démarrer ngrok pour un accès public temporaire",
"step-title-update-grafana-config": "Mettre à jour la configuration Grafana",
"title-setting-connection-could-cause-temporary-outage": "La configuration de cette connexion peut entraîner une interruption temporaire"
"step-title-update-grafana-config": "Mettre à jour la configuration Grafana"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Afficher et gérer vos connexions de mise en service"
@@ -11953,18 +11954,15 @@
},
"home-page": {
"button-delete-repositories": "Supprimer les référentiels",
"configured-repositories-while-running-legacy-storage": "Les référentiels configurés ne fonctionneront pas lors de l’exécution du stockage hérité.",
"confirm-delete-repositories": "Voulez-vous vraiment supprimer tous les référentiels configurés ? Cette action ne peut pas être annulée.",
"error-delete-all-repositories": "Échec de la suppression de tous les référentiels",
"remove-all-configured-repositories": "Supprimer tous les référentiels configurés",
"subtitle": "Afficher et gérer vos référentiels configurés",
"success-all-repositories-deleted": "Tous les référentiels configurés ont été supprimés",
"tab-getting-started": "Pour commencer",
"tab-getting-started-title": "Pour commencer",
"tab-repositories": "Référentiels",
"tab-repositories-title": "Liste des référentiels",
"title-delete-all-configured-repositories": "Supprimer tous les référentiels configurés",
"title-legacy-storage-detected": "Stockage hérité détecté"
"title-delete-all-configured-repositories": "Supprimer tous les référentiels configurés"
},
"http-utils": {
"access-denied": "",
@@ -12004,7 +12002,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12084,7 +12081,6 @@
"repository-status-page": {
"back-to-repositories": "Retour aux référentiels",
"cleaning-up-resources": "Nettoyage des ressources du référentiel",
"legacy-storage-message": "L’instance n’exécute pas encore de stockage unifié ; l’assistant de migration est requis",
"not-found": "introuvable",
"not-found-message": "Référentiel introuvable",
"repository-config-exists-configuration": "Assurez-vous que la configuration du référentiel existe dans le fichier de configuration.",
@@ -12093,7 +12089,6 @@
"tab-resources": "Ressources",
"tab-resources-title": "",
"title": "Statut du référentiel",
"title-legacy-storage": "Stockage hérité",
"title-queued-for-deletion": "Mis en attente pour suppression"
},
"repository-type-cards": {
@@ -12189,9 +12184,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Inclure les commits pour chaque valeur historique",
"synchronization-options": "Options de synchronisation"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "et cliquez",
@@ -12208,11 +12201,11 @@
},
"warning-title-default": "Avertissement",
"wizard": {
"alert-point-1": "Les ressources ne pourront pas être créées, modifiées ou supprimées pendant ce processus. Lors de la dernière étape, elles disparaîtront.",
"alert-point-2": "Une fois la mise en service terminée, les ressources réapparaîtront et seront gérées via un stockage externe.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "La durée de ce processus dépend du nombre de ressources impliquées.",
"alert-point-4": "Les administrateurs d’instances d’entreprise peuvent afficher une bannière d’annonce aux utilisateurs. Consultez <2>ce guide</2> pour obtenir des instructions étape par étape.",
"alert-title": "Important : aucune donnée ou configuration ne sera perdue, mais les tableaux de bord seront temporairement indisponibles pendant quelques minutes.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Terminer",
@@ -12230,7 +12223,6 @@
"step-finish": "Choisir des paramètres supplémentaires",
"step-synchronize": "Synchroniser avec un stockage externe",
"sync-description": "Synchronisez les ressources avec un stockage externe. Après cette étape unique, toutes les futures mises à jour seront automatiquement enregistrées dans le référentiel et mises en service dans l’instance.",
"sync-option-history": "Historique",
"title-bootstrap": "Choisir ce qui doit être synchronisé",
"title-connect": "Se connecter à un stockage externe",
"title-finish": "Choisir des paramètres supplémentaires",
+11 -19
View File
@@ -3329,6 +3329,7 @@
"login-attribute-path-label": "Bejelentkezési attribútum útvonala",
"login-prompt-consent": "Hozzájárulás",
"login-prompt-description": "Jelzi a felhasználói interakció típusát, amikor a felhasználó az IdP-vel jelentkezik be.",
"login-prompt-description-google": "",
"login-prompt-label": "Bejelentkezési felszólítás",
"login-prompt-login": "Bejelentkezés",
"login-prompt-select-account": "Számla kiválasztása",
@@ -3386,6 +3387,7 @@
"use-pkce-description": "Ha engedélyezve van, a Grafana a <2>Proof Key for Code Exchange (PKCE)</2> lehetőséget használja az OAuth2 engedélyezési kód megadásával.",
"use-pkce-label": "PKCE használata",
"use-refresh-token-description": "Ha engedélyezett, a Grafana új hozzáférési tokent fog lekérni az OAuth2-szolgáltató által biztosított frissítési token használatával.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Frissítési token használata",
"validate-hosted-domain-description": "Ha engedélyezett, a Grafana a Google ID-tokennel lekérdezett üzemeltetett tartományt a felhasználó által megadott „{{ allowedDomainsLabel }}” listával próbálja meg egyeztetni.",
"validate-hosted-domain-label": "Üzemeltetett tartomány validálása",
@@ -10181,7 +10183,8 @@
"copy": "Kijelölés másolása",
"disable-menu": "Menü letiltása",
"line-contains": "A hozzáadás sorként szűrőt tartalmaz",
"line-contains-not": "A hozzáadás sorként nem tartalmaz szűrőt"
"line-contains-not": "A hozzáadás sorként nem tartalmaz szűrőt",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Időbélyeg felbontása",
@@ -11871,7 +11874,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Ha az egész példányt csatlakoztatja, az áttelepítés futtatása során az irányítópultok nem lesznek elérhetők. Javasoljuk, hogy a folyamat megkezdése előtt figyelmeztesse a felhasználókat.",
"modal-description-public-access": "Nyilvános hozzáférés beállítása a Grafana-példányhoz a GitHub-integráció engedélyezéséhez",
"modal-description-required-features": "Szükséges Grafana-funkciók engedélyezése a kiépítéshez",
"modal-title-set-up-public-access": "Nyilvános hozzáférés beállítása",
@@ -11883,8 +11885,7 @@
"step-title-copy-url": "A nyilvános URL-cím kimásolása",
"step-title-enable-feature-toggles": "Szükséges funkciókapcsolók engedélyezése",
"step-title-start-ngrok": "Az ngrok elindítása az ideiglenes nyilvános hozzáféréshez",
"step-title-update-grafana-config": "Saját Grafana-konfiguráció frissítése",
"title-setting-connection-could-cause-temporary-outage": "A kapcsolat létrehozása ideiglenes üzemszünetet okozhat"
"step-title-update-grafana-config": "Saját Grafana-konfiguráció frissítése"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Kiépítési kapcsolatok megtekintése és kezelése"
@@ -11953,18 +11954,15 @@
},
"home-page": {
"button-delete-repositories": "Adattárak törlése",
"configured-repositories-while-running-legacy-storage": "A konfigurált adattárak nem fognak működni az örökölt tárolás futtatása közben.",
"confirm-delete-repositories": "Biztosan törli az összes konfigurált adattárat? A művelet nem vonható vissza.",
"error-delete-all-repositories": "Az összes adattár törlése nem sikerült",
"remove-all-configured-repositories": "Az összes konfigurált adattár eltávolítása",
"subtitle": "Konfigurált adattárak megtekintése és kezelése",
"success-all-repositories-deleted": "Az összes konfigurált adattár törölve",
"tab-getting-started": "Kezdés",
"tab-getting-started-title": "Kezdés",
"tab-repositories": "Adattárak",
"tab-repositories-title": "Adattárak listája",
"title-delete-all-configured-repositories": "Az összes konfigurált adattár törlése",
"title-legacy-storage-detected": "Örökölt tárolás észlelve"
"title-delete-all-configured-repositories": "Az összes konfigurált adattár törlése"
},
"http-utils": {
"access-denied": "",
@@ -12004,7 +12002,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12084,7 +12081,6 @@
"repository-status-page": {
"back-to-repositories": "Vissza az adattárakhoz",
"cleaning-up-resources": "Adattári erőforrások tisztítása",
"legacy-storage-message": "A példány még nem fut egyesített tárhellyel – áttelepítési varázslót igényel",
"not-found": "nem található",
"not-found-message": "Nem található adattár",
"repository-config-exists-configuration": "Győződjön meg arról, hogy a tároló konfigurációja létezik a konfigurációs fájlban.",
@@ -12093,7 +12089,6 @@
"tab-resources": "Erőforrások",
"tab-resources-title": "",
"title": "Adattár állapota",
"title-legacy-storage": "Örökölt tárolás",
"title-queued-for-deletion": "Törlésre vár"
},
"repository-type-cards": {
@@ -12189,9 +12184,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Változtatások belefoglalása minden előzményértékhez",
"synchronization-options": "Szinkronizálási beállítások"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "és kattintson erre:",
@@ -12208,11 +12201,11 @@
},
"warning-title-default": "Figyelmeztetés",
"wizard": {
"alert-point-1": "A folyamat során nem lehet erőforrásokat létrehozni, szerkeszteni vagy törölni. Az utolsó lépésben el fognak tűnni.",
"alert-point-2": "A kiépítés befejezése után az erőforrások újra megjelennek, és külső tárolóeszközön keresztül kezelhetők.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "A folyamat időtartama az érintett erőforrások számától függ.",
"alert-point-4": "A vállalati példányok adminisztrátorai megjeleníthetnek egy értesítési szalagot a felhasználóknak. A lépésenkénti utasításokat lásd az <2>útmutatóban</2>.",
"alert-title": "Fontos: Egyetlen adat vagy konfiguráció sem fog elveszni, de az irányítópultok néhány percig átmenetileg nem lesznek elérhetők.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Befejezés",
@@ -12230,7 +12223,6 @@
"step-finish": "Válassza ki a további beállításokat",
"step-synchronize": "Szinkronizálás külső tárolóval",
"sync-description": "Erőforrások szinkronizálása külső tárolóval. Ezután az egyszeri lépés után az összes jövőbeli frissítés automatikusan mentve lesz az adattárba, és vissza lesz építve a példányba.",
"sync-option-history": "Előzmények",
"title-bootstrap": "Válassza ki, mit szeretne szinkronizálni",
"title-connect": "Csatlakozás külső tárolóhoz",
"title-finish": "Válassza ki a további beállításokat",
+11 -19
View File
@@ -3318,6 +3318,7 @@
"login-attribute-path-label": "Jalur atribut login",
"login-prompt-consent": "Persetujuan",
"login-prompt-description": "Menunjukkan jenis interaksi pengguna saat pengguna masuk dengan IdP.",
"login-prompt-description-google": "",
"login-prompt-label": "Permintaan masuk",
"login-prompt-login": "Masuk",
"login-prompt-select-account": "Pilih akun",
@@ -3375,6 +3376,7 @@
"use-pkce-description": "Jika diaktifkan, Grafana akan menggunakan <2>Proof Key for Code Exchange (PKCE)</2> dengan Pemberian Kode Otorisasi OAuth2.",
"use-pkce-label": "Gunakan PKCE",
"use-refresh-token-description": "Jika diaktifkan, Grafana akan mengambil token akses baru menggunakan token muat ulang yang disediakan oleh penyedia OAuth2.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Gunakan token muat ulang",
"validate-hosted-domain-description": "Jika diaktifkan, Grafana akan mencocokkan Domain yang Di-hosting yang diambil dari Token ID Google dengan daftar \"{{ allowedDomainsLabel }}\" yang ditentukan oleh pengguna.",
"validate-hosted-domain-label": "Validasi domain yang di-hosting",
@@ -10138,7 +10140,8 @@
"copy": "Salin pilihan",
"disable-menu": "Nonaktifkan menu",
"line-contains": "Tambahkan sebagai baris berisi filter",
"line-contains-not": "Tambahkan sebagai baris tidak berisi filter"
"line-contains-not": "Tambahkan sebagai baris tidak berisi filter",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Resolusi stempel waktu",
@@ -11820,7 +11823,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Saat Anda menghubungkan seluruh instans, dasbor tidak akan tersedia saat menjalankan migrasi. Sebaiknya peringatkan pengguna Anda sebelum memulai proses.",
"modal-description-public-access": "Siapkan akses publik ke instans Grafana Anda untuk mengaktifkan integrasi GitHub",
"modal-description-required-features": "Aktifkan fitur Grafana yang diperlukan untuk penyediaan",
"modal-title-set-up-public-access": "Atur akses publik",
@@ -11832,8 +11834,7 @@
"step-title-copy-url": "Salin URL publik Anda",
"step-title-enable-feature-toggles": "Aktifkan Tombol Fitur yang Diperlukan",
"step-title-start-ngrok": "Mulai ngrok untuk akses publik sementara",
"step-title-update-grafana-config": "Perbarui konfigurasi Grafana Anda",
"title-setting-connection-could-cause-temporary-outage": "Mengatur koneksi ini dapat menyebabkan pemadaman sementara"
"step-title-update-grafana-config": "Perbarui konfigurasi Grafana Anda"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Lihat dan kelola koneksi penyediaan Anda"
@@ -11902,18 +11903,15 @@
},
"home-page": {
"button-delete-repositories": "Hapus repositori",
"configured-repositories-while-running-legacy-storage": "Repositori yang dikonfigurasi tidak akan berfungsi saat menjalankan penyimpanan lama.",
"confirm-delete-repositories": "Anda yakin ingin menghapus semua repositori yang dikonfigurasi? Tindakan ini tidak dapat dibatalkan.",
"error-delete-all-repositories": "Gagal menghapus semua repositori",
"remove-all-configured-repositories": "Hapus semua repositori yang dikonfigurasi",
"subtitle": "Lihat dan kelola repositori yang dikonfigurasi",
"success-all-repositories-deleted": "Semua repositori yang dikonfigurasi dihapus",
"tab-getting-started": "Memulai",
"tab-getting-started-title": "Memulai",
"tab-repositories": "Repositori",
"tab-repositories-title": "Daftar repositori",
"title-delete-all-configured-repositories": "Hapus semua repositori yang dikonfigurasi",
"title-legacy-storage-detected": "Penyimpanan lama terdeteksi"
"title-delete-all-configured-repositories": "Hapus semua repositori yang dikonfigurasi"
},
"http-utils": {
"access-denied": "",
@@ -11953,7 +11951,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12033,7 +12030,6 @@
"repository-status-page": {
"back-to-repositories": "Kembali ke repositori",
"cleaning-up-resources": "Membersihkan sumber daya repositori",
"legacy-storage-message": "Instans belum menjalankan penyimpanan terpadu -- memerlukan wizard migrasi",
"not-found": "tidak ditemukan",
"not-found-message": "Repositori tidak ditemukan",
"repository-config-exists-configuration": "Pastikan konfigurasi repositori ada dalam file konfigurasi.",
@@ -12042,7 +12038,6 @@
"tab-resources": "Sumber Daya",
"tab-resources-title": "",
"title": "Status Repositori",
"title-legacy-storage": "Penyimpanan Lama",
"title-queued-for-deletion": "Diantrekan untuk dihapus"
},
"repository-type-cards": {
@@ -12138,9 +12133,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Sertakan commit untuk setiap nilai historis",
"synchronization-options": "Opsi sinkronisasi"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "dan klik",
@@ -12157,11 +12150,11 @@
},
"warning-title-default": "Peringatan",
"wizard": {
"alert-point-1": "Sumber daya tidak akan dapat dibuat, diedit, atau dihapus selama proses ini. Pada langkah terakhir, sumber daya akan menghilang.",
"alert-point-2": "Setelah penyediaan selesai, sumber daya akan muncul kembali dan dikelola melalui penyimpanan eksternal.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Durasi proses ini bergantung pada jumlah sumber daya yang terlibat.",
"alert-point-4": "Administrator instans Enterprise dapat menampilkan banner pengumuman kepada pengguna. Lihat <2>panduan ini</2> untuk petunjuk langkah demi langkah.",
"alert-title": "Penting: Tidak ada data atau konfigurasi yang akan hilang, tetapi dasbor tidak akan tersedia sementara selama beberapa menit.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Selesai",
@@ -12179,7 +12172,6 @@
"step-finish": "Pilih pengaturan tambahan",
"step-synchronize": "Sinkronkan dengan penyimpanan eksternal",
"sync-description": "Sinkronkan sumber daya dengan penyimpanan eksternal. Setelah langkah satu kali ini, semua pembaruan mendatang akan disimpan secara otomatis ke repositori dan disediakan kembali ke instans.",
"sync-option-history": "Sejarah",
"title-bootstrap": "Pilih item yang akan disinkronkan",
"title-connect": "Hubungkan ke penyimpanan eksternal",
"title-finish": "Pilih pengaturan tambahan",
+11 -19
View File
@@ -3329,6 +3329,7 @@
"login-attribute-path-label": "Percorso attributo di accesso",
"login-prompt-consent": "Consenso",
"login-prompt-description": "Indica il tipo di interazione dell'utente quando accede con l'IdP.",
"login-prompt-description-google": "",
"login-prompt-label": "Richiesta di accesso",
"login-prompt-login": "Accedi",
"login-prompt-select-account": "Seleziona account",
@@ -3386,6 +3387,7 @@
"use-pkce-description": "Se abilitato, Grafana utilizzerà <2>Proof Key for Code Exchange (PKCE)</2> con il codice di autorizzazione OAuth2.",
"use-pkce-label": "Usa PKCE",
"use-refresh-token-description": "Se abilitata, Grafana recupererà un nuovo token di accesso utilizzando il token di aggiornamento fornito dal provider OAuth2.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Usa token di aggiornamento",
"validate-hosted-domain-description": "Se abilitata, Grafana abbinerà il dominio ospitato recuperato dal token ID Google all'elenco \"{{ allowedDomainsLabel }}\" specificato dall'utente.",
"validate-hosted-domain-label": "Convalida dominio ospitato",
@@ -10181,7 +10183,8 @@
"copy": "Copia la selezione",
"disable-menu": "Disabilita menu",
"line-contains": "Aggiungi come riga contenente filtro",
"line-contains-not": "Aggiungi come riga non contenente filtro"
"line-contains-not": "Aggiungi come riga non contenente filtro",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Risoluzione marca temporale",
@@ -11871,7 +11874,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Quando colleghi l'intera istanza, le dashboard non saranno disponibili durante l'esecuzione della migrazione. Ti consigliamo di avvisare gli utenti prima di avviare il processo.",
"modal-description-public-access": "Configura l'accesso pubblico alla tua istanza Grafana per abilitare l'integrazione con GitHub",
"modal-description-required-features": "Abilita le funzionalità Grafana necessarie per il provisioning",
"modal-title-set-up-public-access": "Configura l'accesso pubblico",
@@ -11883,8 +11885,7 @@
"step-title-copy-url": "Copia il tuo URL pubblico",
"step-title-enable-feature-toggles": "Abilita i toggle della funzionalità richieste",
"step-title-start-ngrok": "Avvia ngrok per l'accesso pubblico temporaneo",
"step-title-update-grafana-config": "Aggiorna la configurazione di Grafana",
"title-setting-connection-could-cause-temporary-outage": "La configurazione di questa connessione potrebbe causare un'interruzione temporanea"
"step-title-update-grafana-config": "Aggiorna la configurazione di Grafana"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Visualizza e gestisci le connessioni di provisioning"
@@ -11953,18 +11954,15 @@
},
"home-page": {
"button-delete-repositories": "Elimina repository",
"configured-repositories-while-running-legacy-storage": "I repository configurati non funzioneranno durante l'esecuzione della memoria esistente.",
"confirm-delete-repositories": "Desideri davvero eliminare tutti i repository configurati? Quest'azione non può essere annullata.",
"error-delete-all-repositories": "Impossibile eliminare tutti i repository",
"remove-all-configured-repositories": "Rimuovi tutti i repository configurati",
"subtitle": "Visualizza e gestisci i repository configurati",
"success-all-repositories-deleted": "Tutti i repository configurati sono stati eliminati",
"tab-getting-started": "Come iniziare",
"tab-getting-started-title": "Come iniziare",
"tab-repositories": "Repository",
"tab-repositories-title": "Elenco dei repository",
"title-delete-all-configured-repositories": "Elimina tutti i repository configurati",
"title-legacy-storage-detected": "Rilevata memoria esistente"
"title-delete-all-configured-repositories": "Elimina tutti i repository configurati"
},
"http-utils": {
"access-denied": "",
@@ -12004,7 +12002,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12084,7 +12081,6 @@
"repository-status-page": {
"back-to-repositories": "Torna ai repository",
"cleaning-up-resources": "Pulizia delle risorse del repository",
"legacy-storage-message": "L'istanza non esegue ancora l'archiviazione unificata: richiede la procedura guidata di migrazione",
"not-found": "non trovato",
"not-found-message": "Repository non trovato",
"repository-config-exists-configuration": "Assicurati che la configurazione del repository esista nel file di configurazione.",
@@ -12093,7 +12089,6 @@
"tab-resources": "Risorse",
"tab-resources-title": "",
"title": "Stato del repository",
"title-legacy-storage": "Memoria esistente",
"title-queued-for-deletion": "In coda per l'eliminazione"
},
"repository-type-cards": {
@@ -12189,9 +12184,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Includi commit per ogni valore storico",
"synchronization-options": "Opzioni di sincronizzazione"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "e fare clic",
@@ -12208,11 +12201,11 @@
},
"warning-title-default": "Attenzione",
"wizard": {
"alert-point-1": "Le risorse non potranno essere create, modificate o eliminate durante questo processo. Nell'ultimo passaggio, scompariranno.",
"alert-point-2": "Una volta completato il provisioning, le risorse riappariranno e verranno gestite tramite la memoria esterna.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "La durata di questo processo dipende dal numero di risorse coinvolte.",
"alert-point-4": "Gli amministratori dell'istanza aziendale possono mostrare un banner di annuncio agli utenti. Consulta <2>questa guida</2> per istruzioni dettagliate.",
"alert-title": "Importante: nessun dato o configurazione andrà perso, ma le dashboard non saranno disponibili per alcuni minuti.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Fine",
@@ -12230,7 +12223,6 @@
"step-finish": "Scegli impostazioni aggiuntive",
"step-synchronize": "Sincronizza con la memoria esterna",
"sync-description": "Sincronizza le risorse con la memoria esterna. Dopo questo passaggio una tantum, tutti gli aggiornamenti futuri verranno salvati automaticamente nel repository e ripristinati nell'istanza.",
"sync-option-history": "Cronologia",
"title-bootstrap": "Scegli cosa sincronizzare",
"title-connect": "Connetti a una memoria esterna",
"title-finish": "Scegli impostazioni aggiuntive",
+11 -19
View File
@@ -3318,6 +3318,7 @@
"login-attribute-path-label": "ログイン属性パス",
"login-prompt-consent": "同意",
"login-prompt-description": "ユーザーがアイデンティティプロバイダー (IdP) でログインするときのユーザーインタラクションのタイプを示します。",
"login-prompt-description-google": "",
"login-prompt-label": "ログインプロンプト",
"login-prompt-login": "ログイン",
"login-prompt-select-account": "アカウントの選択",
@@ -3375,6 +3376,7 @@
"use-pkce-description": "有効にすると、Grafanaは<2>OAuth2認証コード付与でコード交換用証明キー(PKCE)</2>を使用します。",
"use-pkce-label": "PKCEを使用",
"use-refresh-token-description": "有効にすると、GrafanaはOAuth2プロバイダーから提供されたリフレッシュトークンを使用して新しいアクセストークンを取得します。",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "リフレッシュトークンを使用",
"validate-hosted-domain-description": "有効にすると、GrafanaはGoogle IDトークンから取得したホストドメインをユーザーが指定した「{{ allowedDomainsLabel }}」リストと照合します。",
"validate-hosted-domain-label": "ホストドメインを検証",
@@ -10138,7 +10140,8 @@
"copy": "選択した部分をコピー",
"disable-menu": "メニューを無効にする",
"line-contains": "行にフィルタが含まれている場合はフィルタとして追加",
"line-contains-not": "フィルタが含まれていない行として追加"
"line-contains-not": "フィルタが含まれていない行として追加",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "タイムスタンプの表示形式",
@@ -11820,7 +11823,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "インスタンス全体を接続すると、移行実行中はダッシュボードが利用できなくなります。このプロセスを開始する前に、ユーザーに警告することをお勧めします。",
"modal-description-public-access": "GitHub連携を有効にするために、Grafanaインスタンスへの公開アクセスを設定",
"modal-description-required-features": "プロビジョニングに必要なGrafana機能を有効化",
"modal-title-set-up-public-access": "公開アクセスを設定",
@@ -11832,8 +11834,7 @@
"step-title-copy-url": "公開URLをコピー",
"step-title-enable-feature-toggles": "必要な機能トグルを有効化",
"step-title-start-ngrok": "一時的な公開アクセス用にngrokを起動",
"step-title-update-grafana-config": "Grafana設定を更新",
"title-setting-connection-could-cause-temporary-outage": "この接続設定を行うことで、一時的に停止する可能性があります"
"step-title-update-grafana-config": "Grafana設定を更新"
},
"getting-started-page": {
"subtitle-provisioning-feature": "プロビジョニング接続を表示・管理"
@@ -11902,18 +11903,15 @@
},
"home-page": {
"button-delete-repositories": "リポジトリを削除",
"configured-repositories-while-running-legacy-storage": "レガシーストレージの実行中は、設定したリポジトリは機能しません。",
"confirm-delete-repositories": "設定済みのリポジトリをすべて削除してもよろしいですか?この操作は元に戻せません。",
"error-delete-all-repositories": "すべてのリポジトリの削除に失敗しました",
"remove-all-configured-repositories": "設定済みのリポジトリをすべて削除",
"subtitle": "設定済みのリポジトリを表示・管理",
"success-all-repositories-deleted": "設定済みのリポジトリがすべて削除されました",
"tab-getting-started": "はじめに",
"tab-getting-started-title": "はじめに",
"tab-repositories": "リポジトリ",
"tab-repositories-title": "リポジトリ一覧",
"title-delete-all-configured-repositories": "設定済みのリポジトリをすべて削除",
"title-legacy-storage-detected": "レガシーストレージが検出されました"
"title-delete-all-configured-repositories": "設定済みのリポジトリをすべて削除"
},
"http-utils": {
"access-denied": "",
@@ -11953,7 +11951,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12033,7 +12030,6 @@
"repository-status-page": {
"back-to-repositories": "リポジトリに戻る",
"cleaning-up-resources": "リポジトリリソースのクリーンアップ中",
"legacy-storage-message": "インスタンスはまだ統合ストレージを実行していません - 移行ウィザードが必要です",
"not-found": "見つかりません",
"not-found-message": "リポジトリが見つかりません",
"repository-config-exists-configuration": "リポジトリ設定が設定ファイルに含まれていることを確認してください。",
@@ -12042,7 +12038,6 @@
"tab-resources": "リソース",
"tab-resources-title": "",
"title": "リポジトリの状態",
"title-legacy-storage": "レガシーストレージ",
"title-queued-for-deletion": "削除待ちリストに追加されました"
},
"repository-type-cards": {
@@ -12138,9 +12133,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "各履歴値のコミットを含める",
"synchronization-options": "同期オプション"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "そしてクリック",
@@ -12157,11 +12150,11 @@
},
"warning-title-default": "警告",
"wizard": {
"alert-point-1": "このプロセス中はリソースの作成、編集、削除を行えません。最後のステップになると、リソースが消えます。",
"alert-point-2": "プロビジョニングが完了すると、リソースが再表示され、外部ストレージを通じて管理されます。",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "このプロセスの所要時間は、関連するリソースの数によって異なります。",
"alert-point-4": "エンタープライズインスタンス管理者は、ユーザーにお知らせバナーを表示できます。詳しい手順については<2>このガイド</2>をご覧ください。",
"alert-title": "重要:データや設定は失われませんが、ダッシュボードは数分間一時的に利用できなくなります。",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "完了",
@@ -12179,7 +12172,6 @@
"step-finish": "追加設定を選択",
"step-synchronize": "外部ストレージと同期",
"sync-description": "リソースを外部ストレージと同期します。この一度限りの手順が完了すると、その後のすべての更新は自動的にリポジトリに保存され、インスタンスにプロビジョニングされます。",
"sync-option-history": "履歴",
"title-bootstrap": "同期する内容を選択",
"title-connect": "外部ストレージに接続",
"title-finish": "追加設定を選択",
+11 -19
View File
@@ -3318,6 +3318,7 @@
"login-attribute-path-label": "로그인 속성 경로",
"login-prompt-consent": "동의",
"login-prompt-description": "사용자가 IdP로 로그인할 때 어떤 유형의 사용자 상호 작용이 있었는지 보여줍니다.",
"login-prompt-description-google": "",
"login-prompt-label": "로그인 프롬프트",
"login-prompt-login": "로그인",
"login-prompt-select-account": "계정 선택",
@@ -3375,6 +3376,7 @@
"use-pkce-description": "활성화되면 Grafana는 OAuth2 인증 코드 부여와 함께 <2>코드 교환을 위한 증명 키(PKCE)</2>를 사용합니다.",
"use-pkce-label": "PKCE 사용",
"use-refresh-token-description": "활성화되면 Grafana는 OAuth2 제공자가 제공한 새로 고침 토큰을 사용하여 새 액세스 토큰을 가져옵니다.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "새로 고침 토큰 사용",
"validate-hosted-domain-description": "활성화되면 Grafana는 Google ID 토큰에서 검색한 호스팅된 도메인을 사용자가 지정한 \"{{ allowedDomainsLabel }}\" 목록과 일치시킵니다.",
"validate-hosted-domain-label": "호스팅된 도메인 검증",
@@ -10138,7 +10140,8 @@
"copy": "선택 항목 복사",
"disable-menu": "메뉴 비활성화",
"line-contains": "라인에 포함 필터로 추가",
"line-contains-not": "라인에 미포함 필터로 추가"
"line-contains-not": "라인에 미포함 필터로 추가",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "타임스탬프 해상도",
@@ -11820,7 +11823,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "전체 인스턴스를 연결하면 마이그레이션를 실행하는 동안 대시보드를 사용할 수 없습니다. 프로세스를 시작하기 전에 사용자에게 경고하는 것이 좋습니다.",
"modal-description-public-access": "Grafana 인스턴스에 대한 공개 액세스를 설정하여 GitHub 통합을 활성화합니다",
"modal-description-required-features": "프로비저닝에 필요한 Grafana 기능 활성화",
"modal-title-set-up-public-access": "공개 액세스 설정",
@@ -11832,8 +11834,7 @@
"step-title-copy-url": "공개 URL 복사",
"step-title-enable-feature-toggles": "필요한 기능 토글 활성화",
"step-title-start-ngrok": "임시 공용 액세스를 위해 ngrok 시작",
"step-title-update-grafana-config": "Grafana 구성 업데이트",
"title-setting-connection-could-cause-temporary-outage": "이 연결을 설정하면 일시적인 중단이 발생할 수 있습니다"
"step-title-update-grafana-config": "Grafana 구성 업데이트"
},
"getting-started-page": {
"subtitle-provisioning-feature": "프로비저닝 연결 보기 및 관리"
@@ -11902,18 +11903,15 @@
},
"home-page": {
"button-delete-repositories": "리포지토리 삭제",
"configured-repositories-while-running-legacy-storage": "레거시 스토리지를 실행하는 동안에는 구성된 리포지토리가 작동하지 않습니다.",
"confirm-delete-repositories": "정말 구성된 모든 리포지토리를 삭제하시겠어요? 처리 후에는 되돌릴 수 없습니다.",
"error-delete-all-repositories": "모든 리포지토리 삭제 실패",
"remove-all-configured-repositories": "구성된 모든 리포지토리 제거",
"subtitle": "구성된 리포지토리 보기 및 관리",
"success-all-repositories-deleted": "구성된 모든 리포지토리가 삭제되었습니다",
"tab-getting-started": "시작하기",
"tab-getting-started-title": "시작하기",
"tab-repositories": "리포지토리",
"tab-repositories-title": "리포지토리 목록",
"title-delete-all-configured-repositories": "구성된 모든 리포지토리 삭제",
"title-legacy-storage-detected": "레거시 스토리지 감지됨"
"title-delete-all-configured-repositories": "구성된 모든 리포지토리 삭제"
},
"http-utils": {
"access-denied": "",
@@ -11953,7 +11951,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12033,7 +12030,6 @@
"repository-status-page": {
"back-to-repositories": "리포지토리로 돌아가기",
"cleaning-up-resources": "리포지토리 리소스 정리 및 삭제 중",
"legacy-storage-message": "인스턴스가 아직 통합 스토리지를 실행하고 있지 않습니다. 마이그레이션 마법사가 필요합니다",
"not-found": "찾을 수 없음",
"not-found-message": "리포지토리를 찾을 수 없습니다",
"repository-config-exists-configuration": "리포지토리 구성이 구성 파일에 있는지 확인하세요.",
@@ -12042,7 +12038,6 @@
"tab-resources": "리소스",
"tab-resources-title": "",
"title": "리포지토리 상태",
"title-legacy-storage": "레거시 스토리지",
"title-queued-for-deletion": "삭제 대기열에 추가됨"
},
"repository-type-cards": {
@@ -12138,9 +12133,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "각 과거 값에 대한 커밋을 포함합니다",
"synchronization-options": "동기화 옵션"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "및 클릭",
@@ -12157,11 +12150,11 @@
},
"warning-title-default": "경고",
"wizard": {
"alert-point-1": "이 프로세스 중에는 리소스를 생성, 편집 또는 삭제할 수 없습니다. 마지막 단계에서는 리소스가 표시되지 않습니다.",
"alert-point-2": "프로비저닝이 완료되면 리소스가 다시 표시되고 외부 스토리지를 통해 관리됩니다.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "이 프로세스의 지속 시간은 관련된 리소스 수에 따라 달라집니다.",
"alert-point-4": "Enterprise 인스턴스 관리자는 사용자에게 공지 배너를 표시할 수 있습니다. 단계별 지침은 <2>이 가이드</2>를 확인하세요.",
"alert-title": "중요: 데이터나 구성은 손실되지 않지만 대시보드를 몇 분 동안 일시적으로 사용할 수 없습니다.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "완료",
@@ -12179,7 +12172,6 @@
"step-finish": "추가 설정 선택",
"step-synchronize": "외부 스토리지와 동기화",
"sync-description": "외부 스토리지와 리소스를 동기화합니다. 이 일회성 단계 후에는 향후 모든 업데이트가 자동으로 리포지토리에 저장되고 인스턴스에 다시 프로비저닝됩니다.",
"sync-option-history": "이력",
"title-bootstrap": "동기화할 항목 선택",
"title-connect": "외부 스토리지에 연결",
"title-finish": "추가 설정 선택",
+11 -19
View File
@@ -3329,6 +3329,7 @@
"login-attribute-path-label": "Pad inlogattribuut",
"login-prompt-consent": "Toestemming ",
"login-prompt-description": "Geeft het type gebruikersinteractie aan wanneer de gebruiker inlogt met de IdP.",
"login-prompt-description-google": "",
"login-prompt-label": "Login prompt",
"login-prompt-login": "Inloggen",
"login-prompt-select-account": "Selecteer account",
@@ -3386,6 +3387,7 @@
"use-pkce-description": "Indien ingeschakeld, gebruikt Grafana <2>Proof Key for Code Exchange (PKCE)</2> met de OAuth2-autorisatiecodetoewijzing.",
"use-pkce-label": "PKCE gebruiken",
"use-refresh-token-description": "Indien ingeschakeld, haalt Grafana een nieuw toegangstoken op met behulp van het vernieuwingstoken dat is verstrekt door de OAuth2-provider.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Vernieuwingstoken gebruiken",
"validate-hosted-domain-description": "Indien ingeschakeld, zal Grafana het gehoste domein dat is opgehaald uit het Google id-token vergelijken met de '{{ allowedDomainsLabel }}' lijst die is opgegeven door de gebruiker.",
"validate-hosted-domain-label": "Gehost domein valideren",
@@ -10181,7 +10183,8 @@
"copy": "Selectie kopiëren",
"disable-menu": "Menu uitschakelen",
"line-contains": "Toevoegen als regel filter bevat",
"line-contains-not": "Toevoegen als regel geen filter bevat"
"line-contains-not": "Toevoegen als regel geen filter bevat",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Tijdstempelresolutie",
@@ -11871,7 +11874,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Wanneer je je hele instantie koppelt, zijn er geen dashboards beschikbaar tijdens het uitvoeren van de migratie. We raden je aan je gebruikers te waarschuwen voordat je het proces start.",
"modal-description-public-access": "Stel openbare toeging tot je Grafana-instantie in om GitHub-integratie in te schakelen",
"modal-description-required-features": "Vereiste Grafana-functies inschakelen voor provisioning",
"modal-title-set-up-public-access": "Openbare toegang instellen",
@@ -11883,8 +11885,7 @@
"step-title-copy-url": "Kopieer je openbare URL",
"step-title-enable-feature-toggles": "Schakel de vereiste functieschakelaars in",
"step-title-start-ngrok": "Start ngrok voor tijdelijke openbare toegang",
"step-title-update-grafana-config": "Werk je Grafana-configuratie bij",
"title-setting-connection-could-cause-temporary-outage": "Het opzetten van deze verbinding kan een tijdelijke storing veroorzaken"
"step-title-update-grafana-config": "Werk je Grafana-configuratie bij"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Je provisioningverbindingen bekijken en beheren"
@@ -11953,18 +11954,15 @@
},
"home-page": {
"button-delete-repositories": "Repositories verwijderen",
"configured-repositories-while-running-legacy-storage": "Geconfigureerde repositories werken niet tijdens het uitvoeren van legacy-opslag.",
"confirm-delete-repositories": "Weet je zeker dat je alle geconfigureerde repositories wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.",
"error-delete-all-repositories": "Kan niet alle repositories verwijderen",
"remove-all-configured-repositories": "Alle geconfigureerde repositories verwijderen",
"subtitle": "Je geconfigureerde repositories bekijken en beheren",
"success-all-repositories-deleted": "Alle geconfigureerde repositories zijn verwijderd",
"tab-getting-started": "Aan de slag",
"tab-getting-started-title": "Aan de slag",
"tab-repositories": "Repositories",
"tab-repositories-title": "Lijst van repositories",
"title-delete-all-configured-repositories": "Alle geconfigureerde repositories verwijderen",
"title-legacy-storage-detected": "Verouderde opslag gedetecteerd"
"title-delete-all-configured-repositories": "Alle geconfigureerde repositories verwijderen"
},
"http-utils": {
"access-denied": "",
@@ -12004,7 +12002,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12084,7 +12081,6 @@
"repository-status-page": {
"back-to-repositories": "Terug naar repositories",
"cleaning-up-resources": "Bronnen van repository opschonen",
"legacy-storage-message": "Instantie draait nog geen geïntegreerde opslag -- migratiewizard vereist",
"not-found": "niet gevonden",
"not-found-message": "Repository niet gevonden",
"repository-config-exists-configuration": "Zorg ervoor dat de repository-configuratie in het configuratiebestand bestaat.",
@@ -12093,7 +12089,6 @@
"tab-resources": "Bronnen",
"tab-resources-title": "",
"title": "Repository-status",
"title-legacy-storage": "Legacy-opslag",
"title-queued-for-deletion": "In de wachtrij voor verwijdering"
},
"repository-type-cards": {
@@ -12189,9 +12184,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Commits opnemen voor elke historische waarde",
"synchronization-options": "Synchronisatie-opties"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "en klik",
@@ -12208,11 +12201,11 @@
},
"warning-title-default": "Waarschuwing",
"wizard": {
"alert-point-1": "Bronnen kunnen tijdens dit proces niet worden gemaakt, bewerkt of verwijderd. In de laatste stap verdwijnen ze.",
"alert-point-2": "Zodra de provisioning is voltooid, verschijnen bronnen weer en worden ze beheerd via externe opslag.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "De duur van dit proces is afhankelijk van het aantal betrokken bronnen.",
"alert-point-4": "Enterprise-instantiebeheerders kunnen een aankondigingsbanner weergeven aan gebruikers. Zie <2>deze handleiding</2> voor stapsgewijze instructies.",
"alert-title": "Belangrijk: er zullen geen gegevens of configuratie verloren gaan, maar dashboards zullen tijdelijk een paar minuten niet beschikbaar zijn.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Voltooien",
@@ -12230,7 +12223,6 @@
"step-finish": "Kies aanvullende instellingen",
"step-synchronize": "Synchroniseren met externe opslag",
"sync-description": "Bronnen synchroniseren met externe opslag. Na deze eenmalige stap worden alle toekomstige updates automatisch opgeslagen in de repository en opnieuw ingericht in de instantie.",
"sync-option-history": "Geschiedenis",
"title-bootstrap": "Kies wat je wilt synchroniseren",
"title-connect": "Verbinden met externe opslag",
"title-finish": "Kies aanvullende instellingen",
+11 -19
View File
@@ -3351,6 +3351,7 @@
"login-attribute-path-label": "Ścieżka atrybutu loginu",
"login-prompt-consent": "Zgoda",
"login-prompt-description": "Wskazuje rodzaj interakcji użytkownika, gdy użytkownik loguje się za pomocą IdP.",
"login-prompt-description-google": "",
"login-prompt-label": "Monit o zalogowanie",
"login-prompt-login": "Logowanie",
"login-prompt-select-account": "Wybierz konto",
@@ -3408,6 +3409,7 @@
"use-pkce-description": "Jeśli ta opcja jest włączona, Grafana będzie używać <2>klucza próbnego do wymiany kodów (PKCE)</2> z nadawaniem kodu autoryzacyjnego OAuth2.",
"use-pkce-label": "Użyj PKCE",
"use-refresh-token-description": "Jeśli ta opcja jest włączona, Grafana pobiera nowy token dostępu za pomocą tokena odświeżania dostarczonego przez dostawcę OAuth2.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Użyj tokena odświeżania",
"validate-hosted-domain-description": "Jeśli ta opcja jest włączona, Grafana dopasowuje hostowaną domenę pobraną z tokena Google ID do listy „{{ allowedDomainsLabel }}” określonej przez użytkownika.",
"validate-hosted-domain-label": "Zweryfikuj hostowaną domenę",
@@ -10267,7 +10269,8 @@
"copy": "Kopiuj zaznaczenie",
"disable-menu": "Wyłącz menu",
"line-contains": "Dodaj jako wiersz zawierający filtr",
"line-contains-not": "Dodaj jako wiersz nie zawierający filtra"
"line-contains-not": "Dodaj jako wiersz nie zawierający filtra",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Rozdzielczość znacznika czasu",
@@ -11973,7 +11976,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Po połączeniu całej instancji pulpity będą niedostępne podczas uruchamiania migracji. Zalecamy ostrzeżenie użytkowników przed rozpoczęciem tego procesu.",
"modal-description-public-access": "Skonfiguruj publiczny dostęp do instancji Grafany, aby włączyć integrację z GitHubem",
"modal-description-required-features": "Włącz funkcje Grafany wymagane do aprowizacji",
"modal-title-set-up-public-access": "Skonfiguruj dostęp publiczny",
@@ -11985,8 +11987,7 @@
"step-title-copy-url": "Skopiuj swój publiczny URL",
"step-title-enable-feature-toggles": "Włącz wymagane przełączniki funkcji",
"step-title-start-ngrok": "Uruchom narzędzie ngrok, aby uzyskać tymczasowy dostęp publiczny",
"step-title-update-grafana-config": "Zaktualizuj konfigurację usługi Grafana",
"title-setting-connection-could-cause-temporary-outage": "Skonfigurowanie tego połączenia może spowodować tymczasową niedostępność"
"step-title-update-grafana-config": "Zaktualizuj konfigurację usługi Grafana"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Wyświetlaj połączenia aprowizacyjne i nimi zarządzaj"
@@ -12055,18 +12056,15 @@
},
"home-page": {
"button-delete-repositories": "Usuń repozytoria",
"configured-repositories-while-running-legacy-storage": "Skonfigurowane repozytoria nie będą działać podczas uruchamiania starszej pamięci masowej.",
"confirm-delete-repositories": "Czy na pewno chcesz usunąć wszystkie skonfigurowane repozytoria? To działanie jest nieodwracalne.",
"error-delete-all-repositories": "Nie udało się usunąć wszystkich repozytoriów",
"remove-all-configured-repositories": "Usuń wszystkie skonfigurowane repozytoria",
"subtitle": "Wyświetl skonfigurowane repozytoria i nimi zarządzaj",
"success-all-repositories-deleted": "Wszystkie skonfigurowane repozytoria zostały usunięte",
"tab-getting-started": "Pierwsze kroki",
"tab-getting-started-title": "Pierwsze kroki",
"tab-repositories": "Repozytoria",
"tab-repositories-title": "Lista repozytoriów",
"title-delete-all-configured-repositories": "Usuń wszystkie skonfigurowane repozytoria",
"title-legacy-storage-detected": "Wykryto starszą pamięć masową"
"title-delete-all-configured-repositories": "Usuń wszystkie skonfigurowane repozytoria"
},
"http-utils": {
"access-denied": "",
@@ -12106,7 +12104,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12186,7 +12183,6 @@
"repository-status-page": {
"back-to-repositories": "Wróć do repozytoriów",
"cleaning-up-resources": "Sprzątanie zasobów repozytorium",
"legacy-storage-message": "Instancja nie używa jeszcze ujednoliconej pamięci masowej – wymaga to kreatora migracji",
"not-found": "nie znaleziono",
"not-found-message": "Nie znaleziono repozytorium",
"repository-config-exists-configuration": "Upewnij się, że konfiguracja repozytorium istnieje w pliku konfiguracyjnym.",
@@ -12195,7 +12191,6 @@
"tab-resources": "Zasoby",
"tab-resources-title": "",
"title": "Status repozytorium",
"title-legacy-storage": "Starsza pamięć masowa",
"title-queued-for-deletion": "Dodano do kolejki do usunięcia"
},
"repository-type-cards": {
@@ -12291,9 +12286,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Uwzględnij zatwierdzenia dla każdej wartości historycznej",
"synchronization-options": "Opcje synchronizacji"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "i kliknij",
@@ -12310,11 +12303,11 @@
},
"warning-title-default": "Ostrzeżenie",
"wizard": {
"alert-point-1": "Podczas tego procesu nie będzie można tworzyć, edytować ani usuwać zasobów. W ostatnim kroku zasoby znikną.",
"alert-point-2": "Po zakończeniu aprowizacji zasoby pojawią się ponownie i będą zarządzane za pośrednictwem zewnętrznej pamięci masowej.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Czas trwania tego procesu zależy od liczby zasobów.",
"alert-point-4": "Administratorzy instancji Enterprise mogą włączyć wyświetlanie użytkownikom banera z ogłoszeniem. Szczegółowe instrukcje znajdziesz w <2>tym przewodniku</2>.",
"alert-title": "Ważne: dane ani konfiguracja nie zostaną utracone, ale pulpity będą niedostępne przez kilka minut.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Zakończ",
@@ -12332,7 +12325,6 @@
"step-finish": "Wybierz dodatkowe ustawienia",
"step-synchronize": "Synchronizuj z zewnętrzną pamięcią masową",
"sync-description": "Zsynchronizuj zasoby z zewnętrzną pamięcią masową. Po tym jednorazowym kroku wszystkie przyszłe aktualizacje zostaną automatycznie zapisane w repozytorium i ponownie aprowizowane w instancji.",
"sync-option-history": "Historia",
"title-bootstrap": "Wybierz, co chcesz zsynchronizować",
"title-connect": "Połączenie z zewnętrzną pamięcią masową",
"title-finish": "Wybierz dodatkowe ustawienia",
+11 -19
View File
@@ -3329,6 +3329,7 @@
"login-attribute-path-label": "Caminho do atributo de login",
"login-prompt-consent": "Consentimento ",
"login-prompt-description": "Indica o tipo de interação do usuário quando o usuário faz login com o IdP.",
"login-prompt-description-google": "",
"login-prompt-label": "Solicitação de login",
"login-prompt-login": "Login",
"login-prompt-select-account": "Selecionar conta",
@@ -3386,6 +3387,7 @@
"use-pkce-description": "Se estiver habilitada, a Grafana usará a <2>Chave de prova para troca de código (PKCE)</2> com a concessão de código de autorização OAuth2.",
"use-pkce-label": "Usar PKCE",
"use-refresh-token-description": "Se estiver habilitado, o Grafana buscará um novo token de acesso usando o token de atualização fornecido pelo provedor OAuth2.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Usar token de atualização",
"validate-hosted-domain-description": "Se estiver habilitado, o Grafana realizará a comparação entre o Domínio Hospedado obtido a partir do token de ID do Google e a lista \"{{ allowedDomainsLabel }}\" especificada pelo usuário.",
"validate-hosted-domain-label": "Validar domínio hospedado",
@@ -10181,7 +10183,8 @@
"copy": "Copiar seleção",
"disable-menu": "Desativar menu",
"line-contains": "Adicionar como linha contém filtro",
"line-contains-not": "Adicionar como linha não contém filtro"
"line-contains-not": "Adicionar como linha não contém filtro",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Resolução de data e hora",
@@ -11871,7 +11874,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Quando você conectar toda a sua instância, os painéis ficarão indisponíveis durante a execução da migração. Recomendamos que você avise a seus usuários antes de iniciar o processo.",
"modal-description-public-access": "Configure o acesso público à sua instância da Grafana para ativar a integração do GitHub",
"modal-description-required-features": "Ativar os recursos necessários da Grafana para provisionamento",
"modal-title-set-up-public-access": "Configurar acesso público",
@@ -11883,8 +11885,7 @@
"step-title-copy-url": "Copie sua URL pública",
"step-title-enable-feature-toggles": "Ativar as opções dos recursos necessários",
"step-title-start-ngrok": "Inicie o ngrok para acesso público temporário",
"step-title-update-grafana-config": "Atualize sua configuração da Grafana",
"title-setting-connection-could-cause-temporary-outage": "Estabelecer esta conexão pode causar uma interrupção temporária"
"step-title-update-grafana-config": "Atualize sua configuração da Grafana"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Visualize e gerencie suas conexões de provisionamento"
@@ -11953,18 +11954,15 @@
},
"home-page": {
"button-delete-repositories": "Excluir repositórios",
"configured-repositories-while-running-legacy-storage": "Os repositórios configurados não funcionarão durante a execução do armazenamento legado.",
"confirm-delete-repositories": "Tem certeza de que deseja excluir todos os repositórios configurados? Esta ação não poderá ser desfeita. ",
"error-delete-all-repositories": "Falha ao excluir todos os repositórios",
"remove-all-configured-repositories": "Remover todos os repositórios configurados",
"subtitle": "Visualize e gerencie seus repositórios configurados",
"success-all-repositories-deleted": "Todos os repositórios configurados foram excluídos",
"tab-getting-started": "Como começar",
"tab-getting-started-title": "Como começar",
"tab-repositories": "Repositórios",
"tab-repositories-title": "Lista de repositórios",
"title-delete-all-configured-repositories": "Excluir todos os repositórios configurados",
"title-legacy-storage-detected": "Armazenamento legado detectado"
"title-delete-all-configured-repositories": "Excluir todos os repositórios configurados"
},
"http-utils": {
"access-denied": "",
@@ -12004,7 +12002,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12084,7 +12081,6 @@
"repository-status-page": {
"back-to-repositories": "Voltar para os repositórios",
"cleaning-up-resources": "Limpando recursos do repositório",
"legacy-storage-message": "A instância ainda não está executando o armazenamento unificado: requer um assistente de migração",
"not-found": "não encontrado",
"not-found-message": "Repositório não encontrado",
"repository-config-exists-configuration": "Verifique se a configuração do repositório existe no arquivo de configuração.",
@@ -12093,7 +12089,6 @@
"tab-resources": "Fontes",
"tab-resources-title": "",
"title": "Status do repositório",
"title-legacy-storage": "Armazenamento legado",
"title-queued-for-deletion": "Na fila para exclusão"
},
"repository-type-cards": {
@@ -12189,9 +12184,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Incluir confirmações para cada valor histórico",
"synchronization-options": "Opções de sincronização"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "e clique",
@@ -12208,11 +12201,11 @@
},
"warning-title-default": "Aviso",
"wizard": {
"alert-point-1": "Os recursos não poderão ser criados, editados ou excluídos durante este processo. Na última etapa, eles desaparecerão.",
"alert-point-2": "Assim que o provisionamento for concluído, os recursos reaparecerão e serão gerenciados por meio de armazenamento externo.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "A duração deste processo depende da quantidade de recursos envolvidos.",
"alert-point-4": "Os administradores de instâncias corporativas podem exibir um banner de anúncio para os usuários. Consulte <2>este guia</2> para conferir instruções passo a passo.",
"alert-title": "Importante: nenhum dado ou configuração será perdido, mas os painéis ficarão indisponíveis por alguns minutos.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Finalizar",
@@ -12230,7 +12223,6 @@
"step-finish": "Escolha configurações adicionais",
"step-synchronize": "Sincronizar com armazenamento externo",
"sync-description": "Sincronize recursos com armazenamento externo. Após esta única etapa, todas as atualizações futuras serão salvas automaticamente no repositório e provisionadas de volta para a instância.",
"sync-option-history": "Histórico",
"title-bootstrap": "Escolha o que será sincronizado",
"title-connect": "Conectar ao armazenamento externo",
"title-finish": "Escolha configurações adicionais",
+11 -19
View File
@@ -3329,6 +3329,7 @@
"login-attribute-path-label": "Caminho do atributo de início de sessão",
"login-prompt-consent": "Consentimento",
"login-prompt-description": "Indica o tipo de interação do utilizador quando o utilizador inicia sessão com o IdP.",
"login-prompt-description-google": "",
"login-prompt-label": "Instrução de início de sessão",
"login-prompt-login": "Entrar",
"login-prompt-select-account": "Selecione a conta",
@@ -3386,6 +3387,7 @@
"use-pkce-description": "Se estiver ativado, a Grafana utilizará a <2>Chave de prova para troca de código (PKCE)</2> com a concessão do código de autorização OAuth2.",
"use-pkce-label": "Utilizar PKCE",
"use-refresh-token-description": "Se estiver ativado, o Grafana irá obter um novo token de acesso ao utilizar o token de atualização fornecido pelo fornecedor OAuth2.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Utilizar token de atualização",
"validate-hosted-domain-description": "Se estiver ativado, o Grafana irá comparar o domínio alojado obtido do token de ID do Google com a lista \"{{ allowedDomainsLabel }}\" especificada pelo utilizador.",
"validate-hosted-domain-label": "Validar domínio alojado",
@@ -10181,7 +10183,8 @@
"copy": "Copiar seleção",
"disable-menu": "Desativar menu",
"line-contains": "Adicionar, pois a linha contém filtro",
"line-contains-not": "Adicionar, pois a linha não contém filtro"
"line-contains-not": "Adicionar, pois a linha não contém filtro",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Resolução do carimbo de data e hora",
@@ -11871,7 +11874,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Quando liga toda a sua instância, os painéis de controlo ficarão indisponíveis durante a execução da migração. Recomendamos que avise os seus utilizadores antes de iniciar o processo.",
"modal-description-public-access": "Configure o acesso público à sua instância Grafana para ativar a integração do GitHub",
"modal-description-required-features": "Ativar as funcionalidades necessárias da Grafana para aprovisionamento",
"modal-title-set-up-public-access": "Configurar acesso público",
@@ -11883,8 +11885,7 @@
"step-title-copy-url": "Copie o seu URL público",
"step-title-enable-feature-toggles": "Ativar alternâncias de funcionalidades necessárias",
"step-title-start-ngrok": "Inicie a ngrok para um acesso público temporário",
"step-title-update-grafana-config": "Atualizar a sua configuração da Grafana",
"title-setting-connection-could-cause-temporary-outage": "Configurar esta ligação pode causar uma interrupção temporária"
"step-title-update-grafana-config": "Atualizar a sua configuração da Grafana"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Ver e gerir as suas ligações de provisionamento"
@@ -11953,18 +11954,15 @@
},
"home-page": {
"button-delete-repositories": "Eliminar repositórios",
"configured-repositories-while-running-legacy-storage": "Os repositórios configurados não funcionarão durante a execução do armazenamento herdado.",
"confirm-delete-repositories": "Tem a certeza de que pretende eliminar todos os repositórios configurados? Esta ação não pode ser anulada. ",
"error-delete-all-repositories": "Não foi possível eliminar todos os repositórios",
"remove-all-configured-repositories": "Remover todos os repositórios configurados",
"subtitle": "Ver e gerir os seus repositórios configurados",
"success-all-repositories-deleted": "Todos os repositórios configurados eliminados",
"tab-getting-started": "Introdução",
"tab-getting-started-title": "Introdução",
"tab-repositories": "Repositórios",
"tab-repositories-title": "Lista de repositórios",
"title-delete-all-configured-repositories": "Eliminar todos os repositórios configurados",
"title-legacy-storage-detected": "Armazenamento herdado detetado"
"title-delete-all-configured-repositories": "Eliminar todos os repositórios configurados"
},
"http-utils": {
"access-denied": "",
@@ -12004,7 +12002,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12084,7 +12081,6 @@
"repository-status-page": {
"back-to-repositories": "Voltar aos repositórios",
"cleaning-up-resources": "A limpar recursos do repositório",
"legacy-storage-message": "A instância ainda não está a executar o armazenamento unificado; requer o assistente de migração",
"not-found": "não encontrado",
"not-found-message": "Repositório não encontrado",
"repository-config-exists-configuration": "Certifique-se de que a configuração do repositório existe no ficheiro de configuração.",
@@ -12093,7 +12089,6 @@
"tab-resources": "Recursos",
"tab-resources-title": "",
"title": "Estado do repositório",
"title-legacy-storage": "Armazenamento herdado",
"title-queued-for-deletion": "Em fila para eliminação"
},
"repository-type-cards": {
@@ -12189,9 +12184,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Incluir commits para cada valor histórico",
"synchronization-options": "Opções de sincronização"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "e clique em",
@@ -12208,11 +12201,11 @@
},
"warning-title-default": "Aviso",
"wizard": {
"alert-point-1": "Os recursos não poderão ser criados, editados ou eliminados durante este processo. No último passo, irão desaparecer.",
"alert-point-2": "Assim que o provisionamento estiver concluído, os recursos reaparecerão e serão geridos através de um armazenamento externo.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "A duração deste processo depende do número de recursos envolvidos.",
"alert-point-4": "Os administradores de instâncias empresariais podem exibir um banner de anúncio aos utilizadores. Consulte <2>este guia</2> para obter instruções passo a passo.",
"alert-title": "Importante: não serão perdidos dados ou configurações, mas os painéis de controlo ficarão temporariamente indisponíveis durante alguns minutos.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Concluir",
@@ -12230,7 +12223,6 @@
"step-finish": "Escolha as definições adicionais",
"step-synchronize": "Sincronizar com armazenamento externo",
"sync-description": "Sincronize recursos com um armazenamento externo. Após este passo único, todas as atualizações futuras serão guardadas automaticamente no repositório e aprovisionadas novamente para a instância.",
"sync-option-history": "Histórico",
"title-bootstrap": "Escolha o que sincronizar",
"title-connect": "Ligar a um armazenamento externo",
"title-finish": "Escolha as definições adicionais",
+11 -19
View File
@@ -3351,6 +3351,7 @@
"login-attribute-path-label": "Путь атрибута логина",
"login-prompt-consent": "Согласие",
"login-prompt-description": "Указывает тип взаимодействия с пользователем при входе с помощью IdP.",
"login-prompt-description-google": "",
"login-prompt-label": "Приглашение на вход в систему",
"login-prompt-login": "Вход",
"login-prompt-select-account": "Выберите учетную запись",
@@ -3408,6 +3409,7 @@
"use-pkce-description": "Если этот параметр включен, Grafana будет использовать <2>ключ подтверждения для обмена кодом (PKCE)</2> с грантом кода авторизации OAuth2.",
"use-pkce-label": "Использовать PKCE",
"use-refresh-token-description": "Если этот параметр включен, Grafana будет получать новый токен доступа с помощью токена обновления, предоставленного поставщиком OAuth2.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Использовать токен обновления",
"validate-hosted-domain-description": "Если этот параметр включен, Grafana будет сопоставлять домен размещения, полученный из токена Google ID, с заданным пользователем списком «{{ allowedDomainsLabel }}».",
"validate-hosted-domain-label": "Проверять домен размещения",
@@ -10267,7 +10269,8 @@
"copy": "Копировать выделение",
"disable-menu": "Отключить меню",
"line-contains": "Добавить, поскольку строка содержит фильтр",
"line-contains-not": "Добавить, поскольку строка не содержит фильтр"
"line-contains-not": "Добавить, поскольку строка не содержит фильтр",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Разрешение метки времени",
@@ -11973,7 +11976,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Если подключается весь экземпляр, дашборды будут недоступны во время выполнения переноса. Рекомендуем предупредить пользователей перед запуском процесса.",
"modal-description-public-access": "Настройте общий доступ к своему экземпляру Grafana, чтобы включить интеграцию с GitHub.",
"modal-description-required-features": "Включите необходимые функции Grafana для подготовки.",
"modal-title-set-up-public-access": "Настройка общего доступа",
@@ -11985,8 +11987,7 @@
"step-title-copy-url": "Копирование общедоступного URL-адреса",
"step-title-enable-feature-toggles": "Включение необходимых переключателей функциональности",
"step-title-start-ngrok": "Запуск ngrok для временного общего доступа",
"step-title-update-grafana-config": "Обновление конфигурации Grafana",
"title-setting-connection-could-cause-temporary-outage": "Настройка этого подключения может привести к временному сбою"
"step-title-update-grafana-config": "Обновление конфигурации Grafana"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Просмотр подключений для подготовки и управлением ими"
@@ -12055,18 +12056,15 @@
},
"home-page": {
"button-delete-repositories": "Удалить репозитории",
"configured-repositories-while-running-legacy-storage": "Настроенные репозитории не будут работать при использовании устаревшего хранилища.",
"confirm-delete-repositories": "Действительно удалить все настроенные репозитории? Это действие невозможно отменить.",
"error-delete-all-repositories": "Не удалось удалить все репозитории",
"remove-all-configured-repositories": "Удалить все настроенные репозитории",
"subtitle": "Просмотр настроенных репозиториев и управление ими",
"success-all-repositories-deleted": "Все настроенные репозитории удалены",
"tab-getting-started": "Начало работы",
"tab-getting-started-title": "Начало работы",
"tab-repositories": "Репозитории",
"tab-repositories-title": "Список репозиториев",
"title-delete-all-configured-repositories": "Удаление всех настроенных репозиториев",
"title-legacy-storage-detected": "Обнаружено устаревшее хранилище"
"title-delete-all-configured-repositories": "Удаление всех настроенных репозиториев"
},
"http-utils": {
"access-denied": "",
@@ -12106,7 +12104,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12186,7 +12183,6 @@
"repository-status-page": {
"back-to-repositories": "Назад к репозиториям",
"cleaning-up-resources": "Очистка ресурсов репозитория",
"legacy-storage-message": "Экземпляр пока не использует унифицированное хранилище — требуется мастер переноса",
"not-found": "не найдено",
"not-found-message": "Репозиторий не найден",
"repository-config-exists-configuration": "Убедитесь, что конфигурация репозитория существует в файле конфигурации.",
@@ -12195,7 +12191,6 @@
"tab-resources": "Ресурсы",
"tab-resources-title": "",
"title": "Состояние репозитория",
"title-legacy-storage": "Устаревшее хранилище",
"title-queued-for-deletion": "В очереди на удаление"
},
"repository-type-cards": {
@@ -12291,9 +12286,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Включить фиксации для каждого ранее зафиксированного значения",
"synchronization-options": "Параметры синхронизации"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "и нажать",
@@ -12310,11 +12303,11 @@
},
"warning-title-default": "Предупреждение",
"wizard": {
"alert-point-1": "Во время этого процесса невозможно создавать, редактировать или удалять ресурсы. На последнем шаге они исчезнут.",
"alert-point-2": "После завершения подготовки ресурсы снова появятся и будут управляться через внешнее хранилище.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Длительность процесса зависит от количества задействованных ресурсов.",
"alert-point-4": "Администраторы экземпляров Enterprise могут показывать пользователям баннер с объявлением. Пошаговые инструкции см. в <2>руководстве</2>.",
"alert-title": "Важно. Данные и конфигурация не будут потеряны, однако дашборды будут недоступны в течение нескольких минут.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Готово",
@@ -12332,7 +12325,6 @@
"step-finish": "Выберите дополнительные параметры",
"step-synchronize": "Синхронизировать с внешним хранилищем",
"sync-description": "Синхронизируйте ресурсы с внешним хранилищем. После этого разового шага все будущие обновления будут автоматически сохраняться в репозитории и загружаться обратно в экземпляр.",
"sync-option-history": "История",
"title-bootstrap": "Выберите, что синхронизировать",
"title-connect": "Подключение к внешнему хранилищу",
"title-finish": "Выбор дополнительных параметров",
+11 -19
View File
@@ -3329,6 +3329,7 @@
"login-attribute-path-label": "Sökväg för inloggningsattribut",
"login-prompt-consent": "Samtycke",
"login-prompt-description": "Anger typen av användarinteraktion när användaren loggar in med IdP.",
"login-prompt-description-google": "",
"login-prompt-label": "Inloggningsprompt",
"login-prompt-login": "Logga in",
"login-prompt-select-account": "Välj konto",
@@ -3386,6 +3387,7 @@
"use-pkce-description": "Om det är aktiverat kommer Grafana att använda <2>Proof Key for Code Exchange (PKCE)</2> med OAuth2 Authorization Code Grant.",
"use-pkce-label": "Använd PKCE",
"use-refresh-token-description": "Om det är aktiverat hämtar Grafana en ny åtkomsttoken med hjälp av den uppdateringstoken som tillhandahålls av OAuth2-leverantören.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Använd uppdateringstoken",
"validate-hosted-domain-description": "Om det är aktiverat kontrollerar Grafana den värddomän som hämtas från Googles ID-token mot den ”{{ allowedDomainsLabel }}”-lista som användaren har specificerat.",
"validate-hosted-domain-label": "Validera värddomänen",
@@ -10181,7 +10183,8 @@
"copy": "Kopiera markerat",
"disable-menu": "Inaktivera meny",
"line-contains": "Lägg till som rad innehåller filter",
"line-contains-not": "Lägg till som rad innehåller inte filter"
"line-contains-not": "Lägg till som rad innehåller inte filter",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "Tidsstämpelupplösning",
@@ -11871,7 +11874,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "När du ansluter hela din instans kommer instrumentpaneler inte att vara tillgängliga när du kör migreringen. Vi rekommenderar att du varnar dina användare innan du påbörjar processen.",
"modal-description-public-access": "Konfigurera offentlig åtkomst till din Grafana-instans för att aktivera GitHub-integrering",
"modal-description-required-features": "Aktivera obligatoriska Grafana-funktioner för provisionering",
"modal-title-set-up-public-access": "Konfigurera offentlig åtkomst",
@@ -11883,8 +11885,7 @@
"step-title-copy-url": "Kopiera din offentliga webbadress",
"step-title-enable-feature-toggles": "Aktivera obligatoriska funktionsväxlar",
"step-title-start-ngrok": "Starta ngrok för tillfällig offentlig åtkomst",
"step-title-update-grafana-config": "Uppdatera din Grafana-konfiguration",
"title-setting-connection-could-cause-temporary-outage": "Konfiguration av den här anslutningen kan orsaka ett tillfälligt avbrott"
"step-title-update-grafana-config": "Uppdatera din Grafana-konfiguration"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Visa och hantera dina provisioneringsanslutningar"
@@ -11953,18 +11954,15 @@
},
"home-page": {
"button-delete-repositories": "Radera databaser",
"configured-repositories-while-running-legacy-storage": "Konfigurerade lagringsplatser fungerar inte när äldre lagring används.",
"confirm-delete-repositories": "Är du säker på att du vill radera alla konfigurerade databaser? Den här åtgärden kan inte ångras.",
"error-delete-all-repositories": "Det gick inte att radera alla databaser",
"remove-all-configured-repositories": "Ta bort alla konfigurerade databaser",
"subtitle": "Visa och hantera dina konfigurerade databaser",
"success-all-repositories-deleted": "Alla konfigurerade databaser har raderats",
"tab-getting-started": "Kom igång",
"tab-getting-started-title": "Kom igång",
"tab-repositories": "Lagringsplatser",
"tab-repositories-title": "Lista över lagringsplatser",
"title-delete-all-configured-repositories": "Radera alla konfigurerade databaser",
"title-legacy-storage-detected": "Äldre lagring upptäcktes"
"title-delete-all-configured-repositories": "Radera alla konfigurerade databaser"
},
"http-utils": {
"access-denied": "",
@@ -12004,7 +12002,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12084,7 +12081,6 @@
"repository-status-page": {
"back-to-repositories": "Tillbaka till lagringsplatserna",
"cleaning-up-resources": "Rensa lagringsplatsresurser",
"legacy-storage-message": "Instansen kör ännu inte enhetlig lagring – kräver migreringsguide",
"not-found": "hittades inte",
"not-found-message": "Lagringsplatsen hittades inte",
"repository-config-exists-configuration": "Verifiera att lagringsplatskonfigurationen finns i konfigurationsfilen.",
@@ -12093,7 +12089,6 @@
"tab-resources": "Resurser",
"tab-resources-title": "",
"title": "Status för lagringsplats",
"title-legacy-storage": "Äldre lagring",
"title-queued-for-deletion": "Köad för radering"
},
"repository-type-cards": {
@@ -12189,9 +12184,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Inkludera åtaganden för varje historiskt värde",
"synchronization-options": "Synkroniseringsalternativ"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "och klicka på",
@@ -12208,11 +12201,11 @@
},
"warning-title-default": "Varning",
"wizard": {
"alert-point-1": "Resurser kan inte skapas, redigeras eller raderas under den här processen. I det sista steget kommer de att försvinna.",
"alert-point-2": "När provisionering är klar kommer resurserna att visas igen och kan hanteras via extern lagring.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Hur lång den här processen är beror på hur många resurser som är inblandade.",
"alert-point-4": "Enterprise-instansadministratörer kan visa en meddelandebanner för användare. Se <2>denna guide</2> för steg för steg-anvisningar.",
"alert-title": "Viktigt: Inga data eller konfigurationer kommer att förloras, men instrumentpaneler kommer att vara tillfälligt otillgängliga under några minuter.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Slutför",
@@ -12230,7 +12223,6 @@
"step-finish": "Välj ytterligare inställningar",
"step-synchronize": "Synkronisera med extern lagring",
"sync-description": "Synkronisera resurser med extern lagring. Efter det här engångssteget sparas alla framtida uppdateringar automatiskt på lagringsplatsen och provisioneras tillbaka till instansen.",
"sync-option-history": "Historik",
"title-bootstrap": "Välj vad du vill synkronisera",
"title-connect": "Anslut till extern lagring",
"title-finish": "Välj ytterligare inställningar",
+11 -19
View File
@@ -3329,6 +3329,7 @@
"login-attribute-path-label": "Giriş özniteliği yolu",
"login-prompt-consent": "Onay",
"login-prompt-description": "Kullanıcı IdP ile oturum açtığında kullanıcı etkileşiminin türünü gösterir.",
"login-prompt-description-google": "",
"login-prompt-label": "Oturum açma istemi",
"login-prompt-login": "Oturum aç",
"login-prompt-select-account": "Hesap seç",
@@ -3386,6 +3387,7 @@
"use-pkce-description": "Etkinleştirilirse Grafana OAuth2 Yetkilendirme Kodu Verilmesi ile birlikte <2>Proof Key for Code Exchange (PKCE)</2> kullanacaktır.",
"use-pkce-label": "PKCE kullan",
"use-refresh-token-description": "Etkinleştirilirse Grafana, OAuth2 sağlayıcısı tarafından sağlanan yenileme belirtecini kullanarak yeni bir erişim belirteci alır.",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "Yenileme belirteci kullan",
"validate-hosted-domain-description": "Etkinleştirilirse Grafana, Google Kimlik Belirtecinden alınan Barındırılan Alan Adı ile kullanıcı tarafından belirtilen \"{{ allowedDomainsLabel }}\" listesini eşleştirir.",
"validate-hosted-domain-label": "Barındırılan alan adını doğrula",
@@ -10181,7 +10183,8 @@
"copy": "Seçimi kopyala",
"disable-menu": "Menüyü devre dışı bırak",
"line-contains": "Satır içerir filtresi olarak ekle",
"line-contains-not": "Satır içermez filtresi olarak ekle"
"line-contains-not": "Satır içermez filtresi olarak ekle",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "",
@@ -11871,7 +11874,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "Tüm örneğinizi bağladığınızda geçiş işlemi sırasında panolar kullanılamaz hale gelir. İşleme başlamadan önce kullanıcılarınızı bilgilendirmenizi öneririz.",
"modal-description-public-access": "GitHub entegrasyonu için Grafana örneğinizde genel erişimi ayarlayın",
"modal-description-required-features": "Sağlama işlemleri için gerekli Grafana özelliklerini etkinleştir",
"modal-title-set-up-public-access": "Herkese açık erişimi ayarla",
@@ -11883,8 +11885,7 @@
"step-title-copy-url": "Herkese açık URL'nizi kopyalayın",
"step-title-enable-feature-toggles": "Gerekli Özellik Geçişlerini Etkinleştir",
"step-title-start-ngrok": "Geçici genel erişim için ngrok'u başlat",
"step-title-update-grafana-config": "Grafana yapılandırmanızı güncelleyin",
"title-setting-connection-could-cause-temporary-outage": "Bu bağlantıyı kurmak geçici bir kesintiye neden olabilir"
"step-title-update-grafana-config": "Grafana yapılandırmanızı güncelleyin"
},
"getting-started-page": {
"subtitle-provisioning-feature": "Sağlama bağlantılarınızı görüntüleyin ve yönetin"
@@ -11953,18 +11954,15 @@
},
"home-page": {
"button-delete-repositories": "Depoları sil",
"configured-repositories-while-running-legacy-storage": "Eski depolama kullanılırken yapılandırılmış depolar çalışmaz.",
"confirm-delete-repositories": "Yapılandırılmış tüm depoları silmek istediğinizden emin misiniz? Bu işlem geri alınamaz. ",
"error-delete-all-repositories": "Tüm depolar silinemedi",
"remove-all-configured-repositories": "Yapılandırılmış tüm depoları kaldır",
"subtitle": "Yapılandırılmış depoları görüntüleyin ve yönetin",
"success-all-repositories-deleted": "Yapılandırılmış tüm depolar silindi",
"tab-getting-started": "Başlarken",
"tab-getting-started-title": "Başlarken",
"tab-repositories": "Depolar",
"tab-repositories-title": "Depo listesi",
"title-delete-all-configured-repositories": "Yapılandırılmış tüm depoları sil",
"title-legacy-storage-detected": "Eski depolama algılandı"
"title-delete-all-configured-repositories": "Yapılandırılmış tüm depoları sil"
},
"http-utils": {
"access-denied": "",
@@ -12004,7 +12002,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12084,7 +12081,6 @@
"repository-status-page": {
"back-to-repositories": "Depolara geri dön",
"cleaning-up-resources": "Depo kaynakları temizleniyor",
"legacy-storage-message": "Örnek henüz birleşik depolamayı kullanmıyor: Geçiş sihirbazı gerekiyor",
"not-found": "bulunamadı",
"not-found-message": "Depo bulunamadı",
"repository-config-exists-configuration": "Depo yapılandırmasının yapılandırma dosyasında mevcut olduğundan emin olun.",
@@ -12093,7 +12089,6 @@
"tab-resources": "Kaynaklar",
"tab-resources-title": "",
"title": "Depo Durumu",
"title-legacy-storage": "Eski Depolama",
"title-queued-for-deletion": "Silinmek üzere kuyruğa alındı"
},
"repository-type-cards": {
@@ -12189,9 +12184,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "Her geçmiş değer için yürütmeyi dahil et",
"synchronization-options": "Senkronizasyon seçenekleri"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "ve şuna tıklayın:",
@@ -12208,11 +12201,11 @@
},
"warning-title-default": "Uyarı",
"wizard": {
"alert-point-1": "Bu işlem sırasında kaynaklar oluşturulamayacak, düzenlenemeyecek veya silinemeyecek. Son adımda kaynaklar kaybolacaktır.",
"alert-point-2": "Sağlama işlemi tamamlandığında kaynaklar tekrar görünecek ve harici depolama üzerinden yönetilecektir.",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Bu işlemin süresi dâhil olan kaynakların sayısına bağlıdır.",
"alert-point-4": "Enterprise örnek yöneticileri, kullanıcılara bir duyuru afişi gösterebilir. Adım adım talimatlar için <2>bu kılavuza</2> bakın.",
"alert-title": "Önemli: Veri veya yapılandırma kaybı yaşanmayacak ancak panolar birkaç dakika boyunca geçici olarak kullanılamayacaktır.",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "Sonlandır",
@@ -12230,7 +12223,6 @@
"step-finish": "Ek ayarlar seçin",
"step-synchronize": "Harici depolama ile senkronize et",
"sync-description": "Kaynakları harici depolama ile senkronize edin. Bu tek seferlik adımdan sonra tüm gelecekteki güncellemeler otomatik olarak depoya kaydedilecek ve örneğe geri sağlanacaktır.",
"sync-option-history": "Geçmiş",
"title-bootstrap": "Nelerin senkronize edileceğini seçin",
"title-connect": "Harici depolamaya bağlan",
"title-finish": "Ek ayarlar seçin",
+11 -19
View File
@@ -3318,6 +3318,7 @@
"login-attribute-path-label": "登录属性路径",
"login-prompt-consent": "同意",
"login-prompt-description": "指示用户使用 IdP 登录时的用户交互类型。",
"login-prompt-description-google": "",
"login-prompt-label": "登录提示",
"login-prompt-login": "登录",
"login-prompt-select-account": "选择账户",
@@ -3375,6 +3376,7 @@
"use-pkce-description": "如果启用,Grafana 将使用<2>代码交换的证明密钥 (PKCE)</2> 与 OAuth2 身份验证代码授予。",
"use-pkce-label": "使用 PKCE",
"use-refresh-token-description": "如果启用,Grafana 将使用 OAuth2 提供程序提供的刷新令牌获取新的访问令牌。",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "使用刷新令牌",
"validate-hosted-domain-description": "如果启用,Grafana 将把从 Google ID 令牌检索到的托管域与用户指定的“{{ allowedDomainsLabel }}”列表进行匹配。",
"validate-hosted-domain-label": "验证托管域",
@@ -10138,7 +10140,8 @@
"copy": "复制选择",
"disable-menu": "禁用菜单",
"line-contains": "添加为包含筛选器的行",
"line-contains-not": "添加为不包含筛选器的行"
"line-contains-not": "添加为不包含筛选器的行",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "时间戳分辨率",
@@ -11820,7 +11823,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "在连接整个实例的情况下,数据面板在运行迁移时将不可用。我们建议您在开始此流程之前提醒您的用户。",
"modal-description-public-access": "设置对您 Grafana 实例的公共访问以启用 GitHub 集成",
"modal-description-required-features": "启用预配所需的 Grafana 功能",
"modal-title-set-up-public-access": "设置公共访问",
@@ -11832,8 +11834,7 @@
"step-title-copy-url": "复制您的公共 URL",
"step-title-enable-feature-toggles": "启用所需的功能切换",
"step-title-start-ngrok": "启动 ngrok 以实现临时公共访问",
"step-title-update-grafana-config": "更新您的 Grafana 配置",
"title-setting-connection-could-cause-temporary-outage": "设置此连接可能会导致暂时中断"
"step-title-update-grafana-config": "更新您的 Grafana 配置"
},
"getting-started-page": {
"subtitle-provisioning-feature": "查看和管理您的预配连接"
@@ -11902,18 +11903,15 @@
},
"home-page": {
"button-delete-repositories": "删除存储库",
"configured-repositories-while-running-legacy-storage": "运行传统存储时,已配置的存储库将不起作用。",
"confirm-delete-repositories": "您确定要删除所有已配置的存储库吗?此操作无法撤销。",
"error-delete-all-repositories": "删除所有存储库失败",
"remove-all-configured-repositories": "移除所有已配置的存储库",
"subtitle": "查看和管理已配置的存储库",
"success-all-repositories-deleted": "已删除所有已配置的存储库",
"tab-getting-started": "开始操作",
"tab-getting-started-title": "开始操作",
"tab-repositories": "存储库",
"tab-repositories-title": "存储库列表",
"title-delete-all-configured-repositories": "删除所有已配置的存储库",
"title-legacy-storage-detected": "检测到传统存储"
"title-delete-all-configured-repositories": "删除所有已配置的存储库"
},
"http-utils": {
"access-denied": "",
@@ -11953,7 +11951,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12033,7 +12030,6 @@
"repository-status-page": {
"back-to-repositories": "回到存储库",
"cleaning-up-resources": "清理存储库资源",
"legacy-storage-message": "实例尚未运行统一存储 -- 需要迁移向导",
"not-found": "找不到",
"not-found-message": "找不到存储库",
"repository-config-exists-configuration": "确保存储库配置存在于配置文件中。",
@@ -12042,7 +12038,6 @@
"tab-resources": "资源",
"tab-resources-title": "",
"title": "存储库状态",
"title-legacy-storage": "传统存储",
"title-queued-for-deletion": "已进入队列等待删除"
},
"repository-type-cards": {
@@ -12138,9 +12133,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "包括每个历史值的提交",
"synchronization-options": "同步选项"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "并点击",
@@ -12157,11 +12150,11 @@
},
"warning-title-default": "警告",
"wizard": {
"alert-point-1": "在此过程中将无法创建、编辑或删除资源。在最后一步中,它们将消失。",
"alert-point-2": "预配完成后,资源将重新出现并通过外部存储进行管理。",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "此过程的持续时间取决于所涉及资源的数量。",
"alert-point-4": "企业实例管理员可以向用户显示公告横幅。有关分步说明,请参阅<2>本指南</2>。",
"alert-title": "重要提示:数据或配置不会丢失,但数据面板将在几分钟内暂时不可用。",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "完成",
@@ -12179,7 +12172,6 @@
"step-finish": "选择附加设置",
"step-synchronize": "与外部存储同步",
"sync-description": "将资源与外部存储同步。完成此一次性步骤后,所有后续更新都将自动保存到存储库中,并预配回实例。",
"sync-option-history": "历史记录",
"title-bootstrap": "选择要同步的内容",
"title-connect": "连接到外部存储",
"title-finish": "选择附加设置",
+11 -19
View File
@@ -3318,6 +3318,7 @@
"login-attribute-path-label": "登入屬性路徑",
"login-prompt-consent": "同意",
"login-prompt-description": "表示使用者使用 IdP 登入時的使用者互動類型。",
"login-prompt-description-google": "",
"login-prompt-label": "登入提示",
"login-prompt-login": "登入",
"login-prompt-select-account": "選擇帳戶",
@@ -3375,6 +3376,7 @@
"use-pkce-description": "若啟用,Grafana 將使用<2>代碼交換的驗證金鑰 (PKCE)</2> 與 OAuth2 授權碼授權。",
"use-pkce-label": "使用 PKCE",
"use-refresh-token-description": "若啟用,Grafana 將使用 OAuth2 提供者提供的重新整理權杖來擷取新的存取權杖。",
"use-refresh-token-description-google": "",
"use-refresh-token-label": "使用重新整理權杖",
"validate-hosted-domain-description": "若啟用,Grafana 將會比對從 Google ID 權杖擷取的託管網域與使用者指定的「{{ allowedDomainsLabel }}」清單。",
"validate-hosted-domain-label": "驗證託管網域",
@@ -10138,7 +10140,8 @@
"copy": "複製選取範圍",
"disable-menu": "停用功能表",
"line-contains": "新增為包含篩選條件的行",
"line-contains-not": "新增為不包含篩選條件的行"
"line-contains-not": "新增為不包含篩選條件的行",
"search-text": ""
},
"show-log-attributes": "",
"timestamp-format": "時間戳記解決方案",
@@ -11820,7 +11823,6 @@
}
},
"getting-started": {
"alert-temporary-outage": "連接整個執行個體時,在執行移轉時將無法使用儀表板。建議您在開始此流程前通知使用者。",
"modal-description-public-access": "設定 Grafana 執行個體的公共存取權限,以啟用 GitHub 整合",
"modal-description-required-features": "啟用所需的 Grafana 功能以進行佈建",
"modal-title-set-up-public-access": "設定公共存取",
@@ -11832,8 +11834,7 @@
"step-title-copy-url": "複製您的公共網址",
"step-title-enable-feature-toggles": "啟用必要的功能切換",
"step-title-start-ngrok": "啟動 ngrok 以進行臨時公共存取",
"step-title-update-grafana-config": "更新您的 Grafana 設定",
"title-setting-connection-could-cause-temporary-outage": "設定此連線可能會導致暫時中斷"
"step-title-update-grafana-config": "更新您的 Grafana 設定"
},
"getting-started-page": {
"subtitle-provisioning-feature": "檢視及管理您的佈建連線"
@@ -11902,18 +11903,15 @@
},
"home-page": {
"button-delete-repositories": "刪除儲存庫",
"configured-repositories-while-running-legacy-storage": "在執行舊的儲存空間時,已設定的儲存庫將不作用。",
"confirm-delete-repositories": "確定要刪除所有已設定的儲存庫嗎?此動作無法還原。",
"error-delete-all-repositories": "無法刪除所有儲存庫",
"remove-all-configured-repositories": "移除所有已設定的儲存庫",
"subtitle": "檢視及管理您設定的儲存庫",
"success-all-repositories-deleted": "已刪除所有已設定的儲存庫",
"tab-getting-started": "立即開始",
"tab-getting-started-title": "立即開始",
"tab-repositories": "儲存庫",
"tab-repositories-title": "儲存庫清單",
"title-delete-all-configured-repositories": "刪除所有已設定的儲存庫",
"title-legacy-storage-detected": "偵測到舊的儲存空間"
"title-delete-all-configured-repositories": "刪除所有已設定的儲存庫"
},
"http-utils": {
"access-denied": "",
@@ -11953,7 +11951,6 @@
"mode-options": {
"disabled": {
"folder-connected": "",
"legacy-storage": "",
"not-allowed": "",
"not-supported": ""
},
@@ -12033,7 +12030,6 @@
"repository-status-page": {
"back-to-repositories": "返回至儲存庫",
"cleaning-up-resources": "清理儲存庫資源",
"legacy-storage-message": "執行個體尚未執行統一儲存空間 -- 需要移轉小工具",
"not-found": "找不到",
"not-found-message": "找不到儲存庫",
"repository-config-exists-configuration": "請確認儲存庫設定存在於設定檔案中。",
@@ -12042,7 +12038,6 @@
"tab-resources": "資源",
"tab-resources-title": "",
"title": "儲存庫狀態",
"title-legacy-storage": "舊版儲存空間",
"title-queued-for-deletion": "已排入刪除佇列"
},
"repository-type-cards": {
@@ -12138,9 +12133,7 @@
"synchronize-step": {
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": "",
"synchronization-description": "包含每個歷史數值的提交",
"synchronization-options": "同步選項"
"repository-unhealthy": ""
},
"token-permissions-info": {
"and-click": "然後按一下",
@@ -12157,11 +12150,11 @@
},
"warning-title-default": "警告",
"wizard": {
"alert-point-1": "在此過程中,將無法建立、編輯或刪除資源。在最後一步中,它們將會消失。",
"alert-point-2": "佈建完成後,資源將重新出現,並透過外部儲存空間進行管理。",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "此流程的持續時間取決於所涉及的資源數量。",
"alert-point-4": "企業執行個體管理員可以向使用者顯示公告橫幅。請參閱<2>本指南</2>,了解逐步說明。",
"alert-title": "重要事項:不會遺失任何資料或設定,但儀表板將在幾分鐘內暫時無法使用。",
"alert-point-4": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
"button-next": "結束",
@@ -12179,7 +12172,6 @@
"step-finish": "選擇附加設定",
"step-synchronize": "與外部儲存空間同步",
"sync-description": "將資源與外部儲存空間同步。在此一次性步驟之後,未來的所有更新都將自動儲存到儲存庫中,並佈建回執行個體。",
"sync-option-history": "歷史紀錄",
"title-bootstrap": "選擇要同步的內容",
"title-connect": "連接到外部儲存空間",
"title-finish": "選擇附加設定",