From 9849c954a3ebfce892fdd4aa17d9d5a20645ceed Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Wed, 6 Dec 2023 14:00:53 -0800 Subject: [PATCH] Chore: remove the entity kind registry (#79178) --- pkg/server/wire.go | 2 - pkg/services/store/entity/models.go | 48 +---- pkg/services/store/kind/dashboard/summary.go | 8 - pkg/services/store/kind/dataframe/summary.go | 52 ----- .../store/kind/dataframe/summary_test.go | 42 ---- pkg/services/store/kind/dummy/doc.go | 4 - pkg/services/store/kind/dummy/summary.go | 61 ------ pkg/services/store/kind/folder/summary.go | 45 ----- pkg/services/store/kind/geojson/summary.go | 59 ------ .../store/kind/geojson/summary_test.go | 54 ------ pkg/services/store/kind/jsonobj/summary.go | 37 ---- .../store/kind/jsonobj/summary_test.go | 38 ---- pkg/services/store/kind/png/summary.go | 44 ----- pkg/services/store/kind/png/summary_test.go | 33 ---- .../store/kind/preferences/summary.go | 50 ----- pkg/services/store/kind/registry.go | 182 ------------------ pkg/services/store/kind/registry_test.go | 43 ----- pkg/services/store/kind/snapshot/summary.go | 62 ------ pkg/services/store/kind/svg/summary.go | 66 ------- pkg/services/store/validate.go | 4 +- pkg/{services/store/kind/svg => util}/svg.go | 2 +- 21 files changed, 8 insertions(+), 928 deletions(-) delete mode 100644 pkg/services/store/kind/dataframe/summary.go delete mode 100644 pkg/services/store/kind/dataframe/summary_test.go delete mode 100644 pkg/services/store/kind/dummy/doc.go delete mode 100644 pkg/services/store/kind/dummy/summary.go delete mode 100644 pkg/services/store/kind/folder/summary.go delete mode 100644 pkg/services/store/kind/geojson/summary.go delete mode 100644 pkg/services/store/kind/geojson/summary_test.go delete mode 100644 pkg/services/store/kind/jsonobj/summary.go delete mode 100644 pkg/services/store/kind/jsonobj/summary_test.go delete mode 100644 pkg/services/store/kind/png/summary.go delete mode 100644 pkg/services/store/kind/png/summary_test.go delete mode 100644 pkg/services/store/kind/preferences/summary.go delete mode 100644 pkg/services/store/kind/registry.go delete mode 100644 pkg/services/store/kind/registry_test.go delete mode 100644 pkg/services/store/kind/snapshot/summary.go delete mode 100644 pkg/services/store/kind/svg/summary.go rename pkg/{services/store/kind/svg => util}/svg.go (99%) diff --git a/pkg/server/wire.go b/pkg/server/wire.go index 3e2bbf5ba00..23914a8b3e7 100644 --- a/pkg/server/wire.go +++ b/pkg/server/wire.go @@ -136,7 +136,6 @@ import ( "github.com/grafana/grafana/pkg/services/store" entityDB "github.com/grafana/grafana/pkg/services/store/entity/db" "github.com/grafana/grafana/pkg/services/store/entity/sqlstash" - "github.com/grafana/grafana/pkg/services/store/kind" "github.com/grafana/grafana/pkg/services/store/resolver" "github.com/grafana/grafana/pkg/services/store/sanitizer" "github.com/grafana/grafana/pkg/services/supportbundles" @@ -342,7 +341,6 @@ var wireBasicSet = wire.NewSet( grpcserver.ProvideHealthService, grpcserver.ProvideReflectionService, interceptors.ProvideAuthenticator, - kind.ProvideService, // The registry of known kinds entityDB.ProvideEntityDB, wire.Bind(new(sqlstash.EntityDB), new(*entityDB.EntityDB)), sqlstash.ProvideSQLEntityServer, diff --git a/pkg/services/store/entity/models.go b/pkg/services/store/entity/models.go index 25524afc3ac..e973282ebee 100644 --- a/pkg/services/store/entity/models.go +++ b/pkg/services/store/entity/models.go @@ -1,19 +1,15 @@ package entity +import context "context" + //----------------------------------------------------------------------------------------------------- // NOTE: the object store is in heavy development, and the locations will likely continue to move //----------------------------------------------------------------------------------------------------- -import ( - "context" -) - const ( - StandardKindDashboard = "dashboard" - StandardKindPlaylist = "playlist" - StandardKindSnapshot = "snapshot" - StandardKindFolder = "folder" - StandardKindPreferences = "preferences" + StandardKindDashboard = "dashboard" + StandardKindPlaylist = "playlist" + StandardKindFolder = "folder" // StandardKindDataSource: not a real kind yet, but used to define references from dashboards // Types: influx, prometheus, testdata, ... @@ -23,18 +19,6 @@ const ( // Standalone panel is not an object kind yet -- library panel, or nested in dashboard StandardKindPanel = "panel" - // entity.StandardKindSVG SVG file support - StandardKindSVG = "svg" - - // StandardKindPNG PNG file support - StandardKindPNG = "png" - - // StandardKindGeoJSON represents spatial data - StandardKindGeoJSON = "geojson" - - // StandardKindDataFrame data frame - StandardKindDataFrame = "frame" - // StandardKindJSONObj generic json object StandardKindJSONObj = "jsonobj" @@ -64,28 +48,6 @@ const ( ExternalEntityReferenceRuntime_Transformer = "transformer" ) -// EntityKindInfo describes information needed from the object store -// All non-raw types will have a schema that can be used to validate -type EntityKindInfo struct { - // Unique short id for this kind - ID string `json:"id,omitempty"` - - // Display name (may be equal to the ID) - Name string `json:"name,omitempty"` - - // Kind description - Description string `json:"description,omitempty"` - - // The format is not controlled by a schema - IsRaw bool `json:"isRaw,omitempty"` - - // The preferred save extension (svg, png, parquet, etc) if one exists - FileExtension string `json:"fileExtension,omitempty"` - - // The correct mime-type to return for raw objects - MimeType string `json:"mimeType,omitempty"` -} - // EntitySummaryBuilder will read an object, validate it, and return a summary, sanitized payload, or an error // This should not include values that depend on system state, only the raw object type EntitySummaryBuilder = func(ctx context.Context, uid string, body []byte) (*EntitySummary, []byte, error) diff --git a/pkg/services/store/kind/dashboard/summary.go b/pkg/services/store/kind/dashboard/summary.go index 7b845b5faa6..5850bd19fb5 100644 --- a/pkg/services/store/kind/dashboard/summary.go +++ b/pkg/services/store/kind/dashboard/summary.go @@ -11,14 +11,6 @@ import ( "github.com/grafana/grafana/pkg/services/store/entity" ) -func GetEntityKindInfo() entity.EntityKindInfo { - return entity.EntityKindInfo{ - ID: entity.StandardKindDashboard, - Name: "Dashboard", - Description: "Define a grafana dashboard layout", - } -} - // This summary does not resolve old name as UID func GetEntitySummaryBuilder() entity.EntitySummaryBuilder { builder := NewStaticDashboardSummaryBuilder(&directLookup{}, true) diff --git a/pkg/services/store/kind/dataframe/summary.go b/pkg/services/store/kind/dataframe/summary.go deleted file mode 100644 index e4e055c67e7..00000000000 --- a/pkg/services/store/kind/dataframe/summary.go +++ /dev/null @@ -1,52 +0,0 @@ -package dataframe - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/grafana/grafana-plugin-sdk-go/data" - - "github.com/grafana/grafana/pkg/services/store" - "github.com/grafana/grafana/pkg/services/store/entity" -) - -func GetEntityKindInfo() entity.EntityKindInfo { - return entity.EntityKindInfo{ - ID: entity.StandardKindDataFrame, - Name: "Data frame", - Description: "Data frame", - } -} - -func GetEntitySummaryBuilder() entity.EntitySummaryBuilder { - return func(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) { - df := &data.Frame{} - err := json.Unmarshal(body, df) - if err != nil { - return nil, nil, err - } - rows, err := df.RowLen() - if err != nil { - return nil, nil, err - } - - out, err := data.FrameToJSON(df, data.IncludeAll) - if err != nil { - return nil, nil, err - } - summary := &entity.EntitySummary{ - Kind: entity.StandardKindDataFrame, - Name: df.Name, - UID: uid, - Fields: map[string]string{ - "rows": fmt.Sprint(rows), - "cols": fmt.Sprint(len(df.Fields)), - }, - } - if summary.Name == "" { - summary.Name = store.GuessNameFromUID(uid) - } - return summary, out, err - } -} diff --git a/pkg/services/store/kind/dataframe/summary_test.go b/pkg/services/store/kind/dataframe/summary_test.go deleted file mode 100644 index 5fa2b51054a..00000000000 --- a/pkg/services/store/kind/dataframe/summary_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package dataframe - -import ( - "context" - "encoding/json" - "testing" - "time" - - "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/stretchr/testify/require" -) - -func TestDataFrameSummary(t *testing.T) { - df := data.NewFrame("http_requests_total", - data.NewField("timestamp", nil, []time.Time{time.Now(), time.Now(), time.Now()}).SetConfig(&data.FieldConfig{ - DisplayName: "A time Column.", - }), - data.NewField("value", data.Labels{"service": "auth"}, []float64{1.0, 2.0, 3.0}), - data.NewField("category", data.Labels{"service": "auth"}, []string{"foo", "bar", "test"}), - data.NewField("valid", data.Labels{"service": "auth"}, []bool{true, false, true}), - ) - - in, err := data.FrameToJSON(df, data.IncludeAll) - require.NoError(t, err) - - summary, out, err := GetEntitySummaryBuilder()(context.Background(), "somthing", in) - require.NoError(t, err) - require.Equal(t, in, out) // same json - - asjson, err := json.MarshalIndent(summary, "", " ") - // fmt.Printf(string(asjson)) - require.NoError(t, err) - require.JSONEq(t, `{ - "UID": "somthing", - "kind": "frame", - "name": "http_requests_total", - "fields": { - "cols": "4", - "rows": "3" - } - }`, string(asjson)) -} diff --git a/pkg/services/store/kind/dummy/doc.go b/pkg/services/store/kind/dummy/doc.go deleted file mode 100644 index 465cba40540..00000000000 --- a/pkg/services/store/kind/dummy/doc.go +++ /dev/null @@ -1,4 +0,0 @@ -// Package dummy provides a dummy kind useful for testing -// -// The dummy kind returns a complicated summary that can exercise most of the storage options -package dummy diff --git a/pkg/services/store/kind/dummy/summary.go b/pkg/services/store/kind/dummy/summary.go deleted file mode 100644 index 16846eeaadd..00000000000 --- a/pkg/services/store/kind/dummy/summary.go +++ /dev/null @@ -1,61 +0,0 @@ -package dummy - -import ( - "context" - "fmt" - "time" - - "github.com/grafana/grafana/pkg/services/store/entity" -) - -func GetEntityKindInfo(kind string) entity.EntityKindInfo { - return entity.EntityKindInfo{ - ID: kind, - Name: kind, - Description: "Dummy kind used for testing.", - IsRaw: true, - } -} - -func GetEntitySummaryBuilder(kind string) entity.EntitySummaryBuilder { - return func(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) { - summary := &entity.EntitySummary{ - Name: fmt.Sprintf("Dummy: %s", kind), - Kind: kind, - Description: fmt.Sprintf("Wrote at %s", time.Now().Local().String()), - Labels: map[string]string{ - "hello": "world", - "tag1": "", - "tag2": "", - }, - Fields: map[string]string{ - "field1": "a string", - "field2": "1.224", - "field4": "true", - }, - Error: nil, // ignore for now - Nested: nil, // ignore for now - References: []*entity.EntityExternalReference{ - { - Family: "ds", - Type: "influx", - Identifier: "xyz", - }, - { - Family: entity.StandardKindPanel, - Type: "heatmap", - }, - { - Family: entity.StandardKindPanel, - Type: "timeseries", - }, - }, - } - - if summary.UID != "" && uid != summary.UID { - return summary, nil, fmt.Errorf("internal UID mismatch") - } - - return summary, body, nil - } -} diff --git a/pkg/services/store/kind/folder/summary.go b/pkg/services/store/kind/folder/summary.go deleted file mode 100644 index a328e139ace..00000000000 --- a/pkg/services/store/kind/folder/summary.go +++ /dev/null @@ -1,45 +0,0 @@ -package folder - -import ( - "context" - "encoding/json" - - "github.com/grafana/grafana/pkg/services/store" - "github.com/grafana/grafana/pkg/services/store/entity" -) - -type Model struct { - Name string `json:"name"` - Description string `json:"description,omitempty"` -} - -func GetEntityKindInfo() entity.EntityKindInfo { - return entity.EntityKindInfo{ - ID: entity.StandardKindFolder, - Name: "Folder", - } -} - -func GetEntitySummaryBuilder() entity.EntitySummaryBuilder { - return func(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) { - obj := &Model{} - err := json.Unmarshal(body, obj) - if err != nil { - return nil, nil, err // unable to read object - } - - if obj.Name == "" { - obj.Name = store.GuessNameFromUID(uid) - } - - summary := &entity.EntitySummary{ - Kind: entity.StandardKindFolder, - Name: obj.Name, - Description: obj.Description, - UID: uid, - } - - out, err := json.MarshalIndent(obj, "", " ") - return summary, out, err - } -} diff --git a/pkg/services/store/kind/geojson/summary.go b/pkg/services/store/kind/geojson/summary.go deleted file mode 100644 index 4adb5f8cca7..00000000000 --- a/pkg/services/store/kind/geojson/summary.go +++ /dev/null @@ -1,59 +0,0 @@ -package geojson - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/grafana/grafana/pkg/services/store" - "github.com/grafana/grafana/pkg/services/store/entity" -) - -func GetEntityKindInfo() entity.EntityKindInfo { - return entity.EntityKindInfo{ - ID: entity.StandardKindGeoJSON, - Name: "GeoJSON", - Description: "JSON formatted spatial data", - FileExtension: ".geojson", - MimeType: "application/json", - } -} - -// Very basic geojson validator -func GetEntitySummaryBuilder() entity.EntitySummaryBuilder { - return func(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) { - var geojson map[string]any - err := json.Unmarshal(body, &geojson) - if err != nil { - return nil, nil, err - } - - ftype, ok := geojson["type"].(string) - if !ok { - return nil, nil, fmt.Errorf("missing type") - } - - body, err = json.Marshal(geojson) - if err != nil { - return nil, nil, err - } - - summary := &entity.EntitySummary{ - Kind: entity.StandardKindGeoJSON, - Name: store.GuessNameFromUID(uid), - UID: uid, - Fields: map[string]string{ - "type": ftype, - }, - } - - if ftype == "FeatureCollection" { - features, ok := geojson["features"].([]any) - if ok { - summary.Fields["count"] = fmt.Sprint(len(features)) - } - } - - return summary, body, nil - } -} diff --git a/pkg/services/store/kind/geojson/summary_test.go b/pkg/services/store/kind/geojson/summary_test.go deleted file mode 100644 index 57bf3eaa3d9..00000000000 --- a/pkg/services/store/kind/geojson/summary_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package geojson - -import ( - "context" - "encoding/json" - "os" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestGeoJSONSummary(t *testing.T) { - builder := GetEntitySummaryBuilder() - geo := []byte(`{"type":"FeatureCo`) // invalid - _, _, err := builder(context.Background(), "hello", geo) - require.Error(t, err) - - geo = []byte(`{"type":"FeatureCollection","features":[]}`) - summary, out, err := builder(context.Background(), "hello", geo) - require.NoError(t, err) - require.NotEqual(t, geo, out) // wrote json - - asjson, err := json.MarshalIndent(summary, "", " ") - //fmt.Printf(string(asjson)) - require.NoError(t, err) - require.JSONEq(t, `{ - "UID": "hello", - "kind": "geojson", - "name": "hello", - "fields": { - "type": "FeatureCollection", - "count": "0" - } - }`, string(asjson)) - - // Ignore gosec warning G304 since it's a test - // nolint:gosec - airports, err := os.ReadFile("../../../../../public/gazetteer/airports.geojson") - require.NoError(t, err) - summary, _, err = builder(context.Background(), "gaz/airports.geojson", airports) - require.NoError(t, err) - asjson, err = json.MarshalIndent(summary, "", " ") - //fmt.Printf(string(asjson)) - require.NoError(t, err) - require.JSONEq(t, `{ - "UID": "gaz/airports.geojson", - "kind": "geojson", - "name": "airports", - "fields": { - "type": "FeatureCollection", - "count": "888" - } - }`, string(asjson)) -} diff --git a/pkg/services/store/kind/jsonobj/summary.go b/pkg/services/store/kind/jsonobj/summary.go deleted file mode 100644 index 7d05fdc6bfb..00000000000 --- a/pkg/services/store/kind/jsonobj/summary.go +++ /dev/null @@ -1,37 +0,0 @@ -package jsonobj - -import ( - "context" - "encoding/json" - - "github.com/grafana/grafana/pkg/services/store" - "github.com/grafana/grafana/pkg/services/store/entity" -) - -func GetEntityKindInfo() entity.EntityKindInfo { - return entity.EntityKindInfo{ - ID: entity.StandardKindJSONObj, - Name: "JSON Object", - Description: "JSON Object", - } -} - -func GetEntitySummaryBuilder() entity.EntitySummaryBuilder { - return func(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) { - v := make(map[string]any) - err := json.Unmarshal(body, &v) - if err != nil { - return nil, nil, err - } - - out, err := json.MarshalIndent(v, "", " ") - if err != nil { - return nil, nil, err - } - return &entity.EntitySummary{ - Kind: entity.StandardKindJSONObj, - Name: store.GuessNameFromUID(uid), - UID: uid, - }, out, err - } -} diff --git a/pkg/services/store/kind/jsonobj/summary_test.go b/pkg/services/store/kind/jsonobj/summary_test.go deleted file mode 100644 index 661d8079d7f..00000000000 --- a/pkg/services/store/kind/jsonobj/summary_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package jsonobj - -import ( - "context" - "encoding/json" - "testing" - "time" - - "github.com/grafana/grafana-plugin-sdk-go/data" - "github.com/stretchr/testify/require" -) - -func TestDataFrameSummary(t *testing.T) { - // Just creating a JSON blob - df := data.NewFrame("http_requests_total", - data.NewField("timestamp", nil, []time.Time{time.Now(), time.Now(), time.Now()}).SetConfig(&data.FieldConfig{ - DisplayName: "A time Column.", - }), - data.NewField("value", data.Labels{"service": "auth"}, []float64{1.0, 2.0, 3.0}), - data.NewField("category", data.Labels{"service": "auth"}, []string{"foo", "bar", "test"}), - data.NewField("valid", data.Labels{"service": "auth"}, []bool{true, false, true}), - ) - in, err := data.FrameToJSON(df, data.IncludeAll) - require.NoError(t, err) - - summary, out, err := GetEntitySummaryBuilder()(context.Background(), "path/to/item", in) - require.NoError(t, err) - require.JSONEq(t, string(in), string(out)) // same json - - asjson, err := json.MarshalIndent(summary, "", " ") - // fmt.Printf(string(asjson)) - require.NoError(t, err) - require.JSONEq(t, `{ - "name": "item", - "UID": "path/to/item", - "kind": "jsonobj" - }`, string(asjson)) -} diff --git a/pkg/services/store/kind/png/summary.go b/pkg/services/store/kind/png/summary.go deleted file mode 100644 index 4f5d09c09f6..00000000000 --- a/pkg/services/store/kind/png/summary.go +++ /dev/null @@ -1,44 +0,0 @@ -package png - -import ( - "bytes" - "context" - "fmt" - "image/png" - - "github.com/grafana/grafana/pkg/services/store" - "github.com/grafana/grafana/pkg/services/store/entity" -) - -func GetEntityKindInfo() entity.EntityKindInfo { - return entity.EntityKindInfo{ - ID: entity.StandardKindPNG, - Name: "PNG", - Description: "PNG Image file", - IsRaw: true, - FileExtension: "png", - MimeType: "image/png", - } -} - -// SVG sanitizer based on the rendering service -func GetEntitySummaryBuilder() entity.EntitySummaryBuilder { - return func(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) { - img, err := png.Decode(bytes.NewReader(body)) - if err != nil { - return nil, nil, err - } - - size := img.Bounds().Size() - summary := &entity.EntitySummary{ - Kind: entity.StandardKindSVG, - Name: store.GuessNameFromUID(uid), - UID: uid, - Fields: map[string]string{ - "width": fmt.Sprint(size.X), - "height": fmt.Sprint(size.Y), - }, - } - return summary, body, nil - } -} diff --git a/pkg/services/store/kind/png/summary_test.go b/pkg/services/store/kind/png/summary_test.go deleted file mode 100644 index 301c1126ef8..00000000000 --- a/pkg/services/store/kind/png/summary_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package png - -import ( - "context" - "encoding/base64" - "encoding/json" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestPNGSummary(t *testing.T) { - const gopher = `iVBORw0KGgoAAAANSUhEUgAAAEsAAAA8CAAAAAALAhhPAAAFfUlEQVRYw62XeWwUVRzHf2+OPbo9d7tsWyiyaZti6eWGAhISoIGKECEKCAiJJkYTiUgTMYSIosYYBBIUIxoSPIINEBDi2VhwkQrVsj1ESgu9doHWdrul7ba73WNm3vOPtsseM9MdwvvrzTs+8/t95ze/33sI5BqiabU6m9En8oNjduLnAEDLUsQXFF8tQ5oxK3vmnNmDSMtrncks9Hhtt/qeWZapHb1ha3UqYSWVl2ZmpWgaXMXGohQAvmeop3bjTRtv6SgaK/Pb9/bFzUrYslbFAmHPp+3WhAYdr+7GN/YnpN46Opv55VDsJkoEpMrY/vO2BIYQ6LLvm0ThY3MzDzzeSJeeWNyTkgnIE5ePKsvKlcg/0T9QMzXalwXMlj54z4c0rh/mzEfr+FgWEz2w6uk8dkzFAgcARAgNp1ZYef8bH2AgvuStbc2/i6CiWGj98y2tw2l4FAXKkQBIf+exyRnteY83LfEwDQAYCoK+P6bxkZm/0966LxcAAILHB56kgD95PPxltuYcMtFTWw/FKkY/6Opf3GGd9ZF+Qp6mzJxzuRSractOmJrH1u8XTvWFHINNkLQLMR+XHXvfPPHw967raE1xxwtA36IMRfkAAG29/7mLuQcb2WOnsJReZGfpiHsSBX81cvMKywYZHhX5hFPtOqPGWZCXnhWGAu6lX91ElKXSalcLXu3UaOXVay57ZSe5f6Gpx7J2MXAsi7EqSp09b/MirKSyJfnfEEgeDjl8FgDAfvewP03zZ+AJ0m9aFRM8eEHBDRKjfcreDXnZdQuAxXpT2NRJ7xl3UkLBhuVGU16gZiGOgZmrSbRdqkILuL/yYoSXHHkl9KXgqNu3PB8oRg0geC5vFmLjad6mUyTKLmF3OtraWDIfACyXqmephaDABawfpi6tqqBZytfQMqOz6S09iWXhktrRaB8Xz4Yi/8gyABDm5NVe6qq/3VzPrcjELWrebVuyY2T7ar4zQyybUCtsQ5Es1FGaZVrRVQwAgHGW2ZCRZshI5bGQi7HesyE972pOSeMM0dSktlzxRdrlqb3Osa6CCS8IJoQQQgBAbTAa5l5epO34rJszibJI8rxLfGzcp1dRosutGeb2VDNgqYrwTiPNsLxXiPi3dz7LiS1WBRBDBOnqEjyy3aQb+/bLiJzz9dIkscVBBLxMfSEac7kO4Fpkngi0ruNBeSOal+u8jgOuqPz12nryMLCniEjtOOOmpt+KEIqsEdocJjYXwrh9OZqWJQyPCTo67LNS/TdxLAv6R5ZNK9npEjbYdT33gRo4o5oTqR34R+OmaSzDBWsAIPhuRcgyoteNi9gF0KzNYWVItPf2TLoXEg+7isNC7uJkgo1iQWOfRSP9NR11RtbZZ3OMG/VhL6jvx+J1m87+RCfJChAtEBQkSBX2PnSiihc/Twh3j0h7qdYQAoRVsRGmq7HU2QRbaxVGa1D6nIOqaIWRjyRZpHMQKWKpZM5feA+lzC4ZFultV8S6T0mzQGhQohi5I8iw+CsqBSxhFMuwyLgSwbghGb0AiIKkSDmGZVmJSiKihsiyOAUs70UkywooYP0bii9GdH4sfr1UNysd3fUyLLMQN+rsmo3grHl9VNJHbbwxoa47Vw5gupIqrZcjPh9R4Nye3nRDk199V+aetmvVtDRE8/+cbgAAgMIWGb3UA0MGLE9SCbWX670TDy1y98c3D27eppUjsZ6fql3jcd5rUe7+ZIlLNQny3Rd+E5Tct3WVhTM5RBCEdiEK0b6B+/ca2gYU393nFj/n1AygRQxPIUA043M42u85+z2SnssKrPl8Mx76NL3E6eXc3be7OD+H4WHbJkKI8AU8irbITQjZ+0hQcPEgId/Fn/pl9crKH02+5o2b9T/eMx7pKoskYgAAAABJRU5ErkJggg==` - img, err := base64.StdEncoding.DecodeString(gopher) - require.NoError(t, err) - - summary, out, err := GetEntitySummaryBuilder()(context.Background(), "hello.png", img) - require.NoError(t, err) - require.Equal(t, img, out) // same image - - asjson, err := json.MarshalIndent(summary, "", " ") - //fmt.Printf(string(asjson)) - require.NoError(t, err) - require.JSONEq(t, `{ - "UID": "hello.png", - "kind": "svg", - "name": "hello", - "fields": { - "height": "60", - "width": "75" - } - }`, string(asjson)) -} diff --git a/pkg/services/store/kind/preferences/summary.go b/pkg/services/store/kind/preferences/summary.go deleted file mode 100644 index e837bac219a..00000000000 --- a/pkg/services/store/kind/preferences/summary.go +++ /dev/null @@ -1,50 +0,0 @@ -package preferences - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - "github.com/grafana/grafana/pkg/kinds/preferences" - "github.com/grafana/grafana/pkg/services/store/entity" -) - -func GetEntityKindInfo() entity.EntityKindInfo { - return entity.EntityKindInfo{ - ID: entity.StandardKindPreferences, - Name: "Preferences", - } -} - -func GetEntitySummaryBuilder() entity.EntitySummaryBuilder { - return func(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) { - if uid != "default" { - if !(strings.HasPrefix(uid, "user-") || strings.HasPrefix(uid, "team-")) { - return nil, nil, fmt.Errorf("expecting UID: default, user-{#}, or team-{#}") - } - } - - obj := &preferences.Spec{} - err := json.Unmarshal(body, obj) - if err != nil { - return nil, nil, err // unable to read object - } - - summary := &entity.EntitySummary{ - Kind: entity.StandardKindPreferences, - Name: uid, // team-${id} | user-${id} - UID: uid, - } - - if obj.HomeDashboardUID != nil && *obj.HomeDashboardUID != "" { - summary.References = append(summary.References, &entity.EntityExternalReference{ - Family: entity.StandardKindDashboard, - Identifier: *obj.HomeDashboardUID, - }) - } - - out, err := json.MarshalIndent(obj, "", " ") - return summary, out, err - } -} diff --git a/pkg/services/store/kind/registry.go b/pkg/services/store/kind/registry.go deleted file mode 100644 index d6c9f6d27a5..00000000000 --- a/pkg/services/store/kind/registry.go +++ /dev/null @@ -1,182 +0,0 @@ -package kind - -import ( - "fmt" - "sort" - "sync" - - "github.com/grafana/grafana/pkg/services/rendering" - "github.com/grafana/grafana/pkg/services/store/entity" - "github.com/grafana/grafana/pkg/services/store/kind/dashboard" - "github.com/grafana/grafana/pkg/services/store/kind/dataframe" - "github.com/grafana/grafana/pkg/services/store/kind/folder" - "github.com/grafana/grafana/pkg/services/store/kind/geojson" - "github.com/grafana/grafana/pkg/services/store/kind/jsonobj" - "github.com/grafana/grafana/pkg/services/store/kind/png" - "github.com/grafana/grafana/pkg/services/store/kind/preferences" - "github.com/grafana/grafana/pkg/services/store/kind/snapshot" - "github.com/grafana/grafana/pkg/services/store/kind/svg" - "github.com/grafana/grafana/pkg/setting" -) - -type KindRegistry interface { - Register(info entity.EntityKindInfo, builder entity.EntitySummaryBuilder) error - GetSummaryBuilder(kind string) entity.EntitySummaryBuilder - GetInfo(kind string) (entity.EntityKindInfo, error) - GetFromExtension(suffix string) (entity.EntityKindInfo, error) - GetKinds() []entity.EntityKindInfo -} - -func NewKindRegistry() KindRegistry { - kinds := make(map[string]*kindValues) - kinds[entity.StandardKindDashboard] = &kindValues{ - info: dashboard.GetEntityKindInfo(), - builder: dashboard.GetEntitySummaryBuilder(), - } - kinds[entity.StandardKindSnapshot] = &kindValues{ - info: snapshot.GetEntityKindInfo(), - builder: snapshot.GetEntitySummaryBuilder(), - } - kinds[entity.StandardKindFolder] = &kindValues{ - info: folder.GetEntityKindInfo(), - builder: folder.GetEntitySummaryBuilder(), - } - kinds[entity.StandardKindPNG] = &kindValues{ - info: png.GetEntityKindInfo(), - builder: png.GetEntitySummaryBuilder(), - } - kinds[entity.StandardKindGeoJSON] = &kindValues{ - info: geojson.GetEntityKindInfo(), - builder: geojson.GetEntitySummaryBuilder(), - } - kinds[entity.StandardKindDataFrame] = &kindValues{ - info: dataframe.GetEntityKindInfo(), - builder: dataframe.GetEntitySummaryBuilder(), - } - kinds[entity.StandardKindJSONObj] = &kindValues{ - info: jsonobj.GetEntityKindInfo(), - builder: jsonobj.GetEntitySummaryBuilder(), - } - kinds[entity.StandardKindPreferences] = &kindValues{ - info: preferences.GetEntityKindInfo(), - builder: preferences.GetEntitySummaryBuilder(), - } - - // create a registry - reg := ®istry{ - mutex: sync.RWMutex{}, - kinds: kinds, - } - reg.updateInfoArray() - return reg -} - -// TODO? This could be a zero dependency service that others are responsible for configuring -func ProvideService(cfg *setting.Cfg, renderer rendering.Service) KindRegistry { - reg := NewKindRegistry() - - // Register SVG support - //----------------------- - info := svg.GetEntityKindInfo() - allowUnsanitizedSvgUpload := cfg != nil && cfg.Storage.AllowUnsanitizedSvgUpload - support := svg.GetEntitySummaryBuilder(allowUnsanitizedSvgUpload, renderer) - _ = reg.Register(info, support) - - return reg -} - -type kindValues struct { - info entity.EntityKindInfo - builder entity.EntitySummaryBuilder -} - -type registry struct { - mutex sync.RWMutex - kinds map[string]*kindValues - info []entity.EntityKindInfo - suffix map[string]entity.EntityKindInfo -} - -func (r *registry) updateInfoArray() { - suffix := make(map[string]entity.EntityKindInfo) - info := make([]entity.EntityKindInfo, 0, len(r.kinds)) - for _, v := range r.kinds { - info = append(info, v.info) - if v.info.FileExtension != "" { - suffix[v.info.FileExtension] = v.info - } - } - sort.Slice(info, func(i, j int) bool { - return info[i].ID < info[j].ID - }) - r.info = info - r.suffix = suffix -} - -func (r *registry) Register(info entity.EntityKindInfo, builder entity.EntitySummaryBuilder) error { - if info.ID == "" || builder == nil { - return fmt.Errorf("invalid kind") - } - - r.mutex.Lock() - defer r.mutex.Unlock() - - if r.kinds[info.ID] != nil { - return fmt.Errorf("already exits") - } - - r.kinds[info.ID] = &kindValues{ - info: info, - builder: builder, - } - r.updateInfoArray() - return nil -} - -// GetSummaryBuilder returns a builder or nil if not found -func (r *registry) GetSummaryBuilder(kind string) entity.EntitySummaryBuilder { - r.mutex.RLock() - defer r.mutex.RUnlock() - - v, ok := r.kinds[kind] - if !ok { - // fallback to default - v, ok = r.kinds[entity.StandardKindJSONObj] - } - if ok { - return v.builder - } - return nil -} - -// GetInfo returns the registered info -func (r *registry) GetInfo(kind string) (entity.EntityKindInfo, error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - - v, ok := r.kinds[kind] - if ok { - return v.info, nil - } - return entity.EntityKindInfo{}, fmt.Errorf("not found") -} - -// GetInfo returns the registered info -func (r *registry) GetFromExtension(suffix string) (entity.EntityKindInfo, error) { - r.mutex.RLock() - defer r.mutex.RUnlock() - - v, ok := r.suffix[suffix] - if ok { - return v, nil - } - return entity.EntityKindInfo{}, fmt.Errorf("not found") -} - -// GetSummaryBuilder returns a builder or nil if not found -func (r *registry) GetKinds() []entity.EntityKindInfo { - r.mutex.RLock() - defer r.mutex.RUnlock() - - return r.info // returns a copy of the array -} diff --git a/pkg/services/store/kind/registry_test.go b/pkg/services/store/kind/registry_test.go deleted file mode 100644 index a9e501ed5f8..00000000000 --- a/pkg/services/store/kind/registry_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package kind - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/grafana/grafana/pkg/services/store/kind/dummy" -) - -func TestKindRegistry(t *testing.T) { - registry := NewKindRegistry() - err := registry.Register(dummy.GetEntityKindInfo("test"), dummy.GetEntitySummaryBuilder("test")) - require.NoError(t, err) - - ids := []string{} - for _, k := range registry.GetKinds() { - ids = append(ids, k.ID) - } - require.Equal(t, []string{ - "dashboard", - "folder", - "frame", - "geojson", - "jsonobj", - "png", - "preferences", - "snapshot", - "test", - }, ids) - - // Check that we registered a test item - info, err := registry.GetInfo("test") - require.NoError(t, err) - require.Equal(t, "test", info.Name) - require.True(t, info.IsRaw) - - // Get by suffix - info, err = registry.GetFromExtension("png") - require.NoError(t, err) - require.Equal(t, "PNG", info.Name) - require.True(t, info.IsRaw) -} diff --git a/pkg/services/store/kind/snapshot/summary.go b/pkg/services/store/kind/snapshot/summary.go deleted file mode 100644 index 8cde29d3268..00000000000 --- a/pkg/services/store/kind/snapshot/summary.go +++ /dev/null @@ -1,62 +0,0 @@ -package snapshot - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/grafana/grafana/pkg/services/store/entity" -) - -// A snapshot is a dashboard with no external queries and a few additional properties -type Model struct { - Name string `json:"name"` - Description string `json:"description,omitempty"` - DeleteKey string `json:"deleteKey"` - ExternalURL string `json:"externalURL"` - Expires int64 `json:"expires,omitempty"` // time that this expires - DashboardUID string `json:"dashboard,omitempty"` - Snapshot json.RawMessage `json:"snapshot,omitempty"` -} - -func GetEntityKindInfo() entity.EntityKindInfo { - return entity.EntityKindInfo{ - ID: entity.StandardKindSnapshot, - Name: "Snapshot", - } -} - -func GetEntitySummaryBuilder() entity.EntitySummaryBuilder { - return func(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) { - obj := &Model{} - err := json.Unmarshal(body, obj) - if err != nil { - return nil, nil, err // unable to read object - } - - if obj.Name == "" { - return nil, nil, fmt.Errorf("expected snapshot name") - } - if obj.DeleteKey == "" { - return nil, nil, fmt.Errorf("expected delete key") - } - - summary := &entity.EntitySummary{ - Kind: entity.StandardKindFolder, - Name: obj.Name, - Description: obj.Description, - UID: uid, - Fields: map[string]string{ - "deleteKey": obj.DeleteKey, - "externalURL": obj.ExternalURL, - "expires": fmt.Sprint(obj.Expires), - }, - References: []*entity.EntityExternalReference{ - {Family: entity.StandardKindDashboard, Identifier: obj.DashboardUID}, - }, - } - - // Keep the original body - return summary, body, err - } -} diff --git a/pkg/services/store/kind/svg/summary.go b/pkg/services/store/kind/svg/summary.go deleted file mode 100644 index 474a81fb5e2..00000000000 --- a/pkg/services/store/kind/svg/summary.go +++ /dev/null @@ -1,66 +0,0 @@ -package svg - -import ( - "context" - "fmt" - "strings" - - "github.com/grafana/grafana/pkg/services/rendering" - "github.com/grafana/grafana/pkg/services/store/entity" -) - -func GetEntityKindInfo() entity.EntityKindInfo { - return entity.EntityKindInfo{ - ID: entity.StandardKindSVG, - Name: "SVG", - Description: "Scalable Vector Graphics", - IsRaw: true, - FileExtension: "svg", - MimeType: "image/svg+xml", - } -} - -// SVG sanitizer based on the rendering service -func GetEntitySummaryBuilder(allowUnsanitizedSvgUpload bool, renderer rendering.Service) entity.EntitySummaryBuilder { - return func(ctx context.Context, uid string, body []byte) (*entity.EntitySummary, []byte, error) { - if !IsSVG(body) { - return nil, nil, fmt.Errorf("invalid svg") - } - - // When a renderer exists, we can return a sanitized version - var sanitized []byte - if renderer != nil { - rsp, err := renderer.SanitizeSVG(ctx, &rendering.SanitizeSVGRequest{ - Content: body, - }) - if err != nil && !allowUnsanitizedSvgUpload { - return nil, nil, err - } - sanitized = rsp.Sanitized - } - if sanitized == nil { - if !allowUnsanitizedSvgUpload { - return nil, nil, fmt.Errorf("unable to sanitize svg") - } - sanitized = body - } - - return &entity.EntitySummary{ - Kind: entity.StandardKindSVG, - Name: guessNameFromUID(uid), - UID: uid, - }, sanitized, nil - } -} - -func guessNameFromUID(uid string) string { - sidx := strings.LastIndex(uid, "/") + 1 - didx := strings.LastIndex(uid, ".") - if didx > sidx && didx != sidx { - return uid[sidx:didx] - } - if sidx > 0 { - return uid[sidx:] - } - return uid -} diff --git a/pkg/services/store/validate.go b/pkg/services/store/validate.go index 00fa3eef4a9..f1c957b8029 100644 --- a/pkg/services/store/validate.go +++ b/pkg/services/store/validate.go @@ -9,8 +9,8 @@ import ( "strings" "github.com/grafana/grafana/pkg/infra/filestorage" - "github.com/grafana/grafana/pkg/services/store/kind/svg" "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/util" ) var ( @@ -52,7 +52,7 @@ func fail(reason string) validationResult { func (s *standardStorageService) detectMimeType(ctx context.Context, user *user.SignedInUser, uploadRequest *UploadRequest) string { if strings.HasSuffix(uploadRequest.Path, ".svg") { - if svg.IsSVG(uploadRequest.Contents) { + if util.IsSVG(uploadRequest.Contents) { return "image/svg+xml" } } diff --git a/pkg/services/store/kind/svg/svg.go b/pkg/util/svg.go similarity index 99% rename from pkg/services/store/kind/svg/svg.go rename to pkg/util/svg.go index 44609814442..356c3f080cd 100644 --- a/pkg/services/store/kind/svg/svg.go +++ b/pkg/util/svg.go @@ -23,7 +23,7 @@ //FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR //OTHER DEALINGS IN THE SOFTWARE. -package svg +package util import ( "regexp"