@ -11,10 +11,8 @@ import (
"github.com/stretchr/testify/require"
"google.golang.org/grpc/metadata"
"github.com/grafana/grafana/pkg/infra/grn"
"github.com/grafana/grafana/pkg/services/store"
"github.com/grafana/grafana/pkg/services/store/entity"
"github.com/grafana/grafana/pkg/util"
)
var (
@ -25,7 +23,7 @@ var (
)
type rawEntityMatcher struct {
grn * grn . GRN
key string
createdRange [ ] time . Time
updatedRange [ ] time . Time
createdBy string
@ -53,16 +51,8 @@ func requireEntityMatch(t *testing.T, obj *entity.Entity, m rawEntityMatcher) {
require . NotNil ( t , obj )
mismatches := ""
if m . grn != nil {
if m . grn . TenantID > 0 && m . grn . TenantID != obj . GRN . TenantID {
mismatches += fmt . Sprintf ( "expected tenant: %d, actual: %d\n" , m . grn . TenantID , obj . GRN . TenantID )
}
if m . grn . ResourceKind != "" && m . grn . ResourceKind != obj . GRN . ResourceKind {
mismatches += fmt . Sprintf ( "expected ResourceKind: %s, actual: %s\n" , m . grn . ResourceKind , obj . GRN . ResourceKind )
}
if m . grn . ResourceIdentifier != "" && m . grn . ResourceIdentifier != obj . GRN . ResourceIdentifier {
mismatches += fmt . Sprintf ( "expected ResourceIdentifier: %s, actual: %s\n" , m . grn . ResourceIdentifier , obj . GRN . ResourceIdentifier )
}
if m . key != "" && m . key != obj . Key {
mismatches += fmt . Sprintf ( "expected key: %s, actual: %s\n" , m . key , obj . Key )
}
if len ( m . createdRange ) == 2 && ! timestampInRange ( obj . CreatedAt , m . createdRange ) {
@ -134,59 +124,63 @@ func TestIntegrationEntityServer(t *testing.T) {
fakeUser := store . GetUserIDString ( testCtx . user )
firstVersion := "1"
kind := entity . StandardKindJSONObj
testGrn := & grn . GRN {
ResourceKind : kind ,
ResourceIdentifier : "my-test-entity" ,
}
group := "test.grafana.app"
resource := "jsonobjs"
resource2 := "playlists"
namespace := "default"
uid := "my-test-entity"
testKey := "/" + group + "/" + resource + "/" + namespace + "/" + uid
body := [ ] byte ( "{\"name\":\"John\"}" )
t . Run ( "should not retrieve non-existent objects" , func ( t * testing . T ) {
resp , err := testCtx . client . Read ( ctx , & entity . ReadEntityRequest {
GRN : testGrn ,
Key : testKey ,
} )
require . NoError ( t , err )
require . NotNil ( t , resp )
require . Nil ( t , resp . GRN )
require . Empty ( t , resp . Key )
} )
t . Run ( "should be able to read persisted objects" , func ( t * testing . T ) {
before := time . Now ( )
wri teReq := & entity . Wri teEntityRequest{
crea teReq := & entity . Crea teEntityRequest{
Entity : & entity . Entity {
GRN : testGrn ,
Body : body ,
Message : "first entity!" ,
Key : testKey ,
Group : group ,
Resource : resource ,
Namespace : namespace ,
Uid : uid ,
Body : body ,
Message : "first entity!" ,
} ,
}
writeResp , err := testCtx . client . Write ( ctx , writeReq )
crea teResp, err := testCtx . client . Crea te( ctx , crea teReq)
require . NoError ( t , err )
versionMatcher := objectVersionMatcher {
updatedRange : [ ] time . Time { before , time . Now ( ) } ,
updatedBy : fakeUser ,
version : & firstVersion ,
comment : & wri teReq. Entity . Message ,
comment : & crea teReq. Entity . Message ,
}
requireVersionMatch ( t , wri teResp. Entity , versionMatcher )
requireVersionMatch ( t , crea teResp. Entity , versionMatcher )
readResp , err := testCtx . client . Read ( ctx , & entity . ReadEntityRequest {
GRN : testGrn ,
Key : testKey ,
Version : "" ,
WithBody : true ,
} )
require . NoError ( t , err )
require . NotNil ( t , readResp )
foundGRN := readResp . GRN
require . NotNil ( t , foundGRN )
require . Equal ( t , testCtx . user . OrgID , foundGRN . TenantID ) // orgId becomes the tenant id when not set
require . Equal ( t , testGrn . ResourceKind , foundGRN . ResourceKind )
require . Equal ( t , testGrn . ResourceIdentifier , foundGRN . ResourceIdentifier )
require . Equal ( t , testKey , readResp . Key )
require . Equal ( t , namespace , readResp . Namespace ) // orgId becomes the tenant id when not set
require . Equal ( t , resource , readResp . Resource )
require . Equal ( t , uid , readResp . Uid )
objectMatcher := rawEntityMatcher {
grn : testGrn ,
key : testKey ,
createdRange : [ ] time . Time { before , time . Now ( ) } ,
updatedRange : [ ] time . Time { before , time . Now ( ) } ,
createdBy : fakeUser ,
@ -197,74 +191,74 @@ func TestIntegrationEntityServer(t *testing.T) {
requireEntityMatch ( t , readResp , objectMatcher )
deleteResp , err := testCtx . client . Delete ( ctx , & entity . DeleteEntityRequest {
GRN : testGrn ,
PreviousVersion : w rit eResp. Entity . Version ,
Key : testKey ,
PreviousVersion : read Resp . Version ,
} )
require . NoError ( t , err )
require . Equal ( t , deleteResp . Status , entity . DeleteEntityResponse_DELETED )
readRespAfterDelete , err := testCtx . client . Read ( ctx , & entity . ReadEntityRequest {
GRN : testGrn ,
Key : testKey ,
Version : "" ,
WithBody : true ,
} )
require . NoError ( t , err )
require . Nil ( t , readRespAfterDelete . GRN )
require . Empty ( t , readRespAfterDelete . Key )
} )
t . Run ( "should be able to update an object" , func ( t * testing . T ) {
before := time . Now ( )
testGrn := & grn . GRN {
ResourceKind : kind ,
ResourceIdentifier : util . GenerateShortUID ( ) ,
}
writeReq1 := & entity . Wri teEntityRequest{
createReq := & entity . CreateEntityRequest {
Entity : & entity . Entity {
GRN : testGrn ,
Body : body ,
Message : "first entity!" ,
Key : testKey ,
Group : group ,
Resource : resource ,
Namespace : namespace ,
Uid : uid ,
Body : body ,
Message : "first entity!" ,
} ,
}
writeResp1 , err := testCtx . client . Write ( ctx , writeReq1 )
createResp , err := testCtx . client . Crea te( ctx , createReq )
require . NoError ( t , err )
require . Equal ( t , entity . WriteEntityResponse_CREATED , writeResp1 . Status )
require . Equal ( t , entity . CreateEntityResponse_CREATED , createResp . Status )
body2 := [ ] byte ( "{\"name\":\"John2\"}" )
writeReq2 := & entity . Wri teEntityRequest{
updateReq := & entity . Upda teEntityRequest{
Entity : & entity . Entity {
GRN : testGrn ,
Key : testKey ,
Body : body2 ,
Message : "update1" ,
} ,
}
writeResp2 , err := testCtx . client . Wri te( ctx , writeReq2 )
updateResp , err := testCtx . client . Upda te( ctx , updateReq )
require . NoError ( t , err )
require . NotEqual ( t , writeResp1 . Entity . Version , writeResp2 . Entity . Version )
require . NotEqual ( t , createResp . Entity . Version , updateResp . Entity . Version )
// Duplicate write (no change)
writeDupRsp , err := testCtx . client . Wri te( ctx , writeReq2 )
writeDupRsp , err := testCtx . client . Upda te( ctx , updateReq )
require . NoError ( t , err )
require . Nil ( t , writeDupRsp . Error )
require . Equal ( t , entity . Wri teEntityResponse_UNCHANGED, writeDupRsp . Status )
require . Equal ( t , writeResp2 . Entity . Version , writeDupRsp . Entity . Version )
require . Equal ( t , writeResp2 . Entity . ETag , writeDupRsp . Entity . ETag )
require . Equal ( t , entity . Upda teEntityResponse_UNCHANGED, writeDupRsp . Status )
require . Equal ( t , updateResp . Entity . Version , writeDupRsp . Entity . Version )
require . Equal ( t , updateResp . Entity . ETag , writeDupRsp . Entity . ETag )
body3 := [ ] byte ( "{\"name\":\"John3\"}" )
writeReq3 := & entity . Wri teEntityRequest{
writeReq3 := & entity . Upda teEntityRequest{
Entity : & entity . Entity {
GRN : testGrn ,
Key : testKey ,
Body : body3 ,
Message : "update3" ,
} ,
}
writeResp3 , err := testCtx . client . Wri te( ctx , writeReq3 )
writeResp3 , err := testCtx . client . Upda te( ctx , writeReq3 )
require . NoError ( t , err )
require . NotEqual ( t , writeResp3 . Entity . Version , writeResp2 . Entity . Version )
require . NotEqual ( t , writeResp3 . Entity . Version , updateResp . Entity . Version )
latestMatcher := rawEntityMatcher {
grn : testGrn ,
key : testKey ,
createdRange : [ ] time . Time { before , time . Now ( ) } ,
updatedRange : [ ] time . Time { before , time . Now ( ) } ,
createdBy : fakeUser ,
@ -273,7 +267,7 @@ func TestIntegrationEntityServer(t *testing.T) {
version : & writeResp3 . Entity . Version ,
}
readRespLatest , err := testCtx . client . Read ( ctx , & entity . ReadEntityRequest {
GRN : testGrn ,
Key : testKey ,
Version : "" , // latest
WithBody : true ,
} )
@ -281,15 +275,15 @@ func TestIntegrationEntityServer(t *testing.T) {
requireEntityMatch ( t , readRespLatest , latestMatcher )
readRespFirstVer , err := testCtx . client . Read ( ctx , & entity . ReadEntityRequest {
GRN : testGrn ,
Version : writeResp1 . Entity . Version ,
Key : testKey ,
Version : createResp . Entity . Version ,
WithBody : true ,
} )
require . NoError ( t , err )
require . NotNil ( t , readRespFirstVer )
requireEntityMatch ( t , readRespFirstVer , rawEntityMatcher {
grn : testGrn ,
key : testKey ,
createdRange : [ ] time . Time { before , time . Now ( ) } ,
updatedRange : [ ] time . Time { before , time . Now ( ) } ,
createdBy : fakeUser ,
@ -299,17 +293,17 @@ func TestIntegrationEntityServer(t *testing.T) {
} )
history , err := testCtx . client . History ( ctx , & entity . EntityHistoryRequest {
GRN : testGrn ,
Key : testKey ,
} )
require . NoError ( t , err )
require . Equal ( t , [ ] * entity . Entity {
writeResp3 . Entity ,
writeResp2 . Entity ,
writeResp1 . Entity ,
updateResp . Entity ,
createResp . Entity ,
} , history . Versions )
deleteResp , err := testCtx . client . Delete ( ctx , & entity . DeleteEntityRequest {
GRN : testGrn ,
Key : testKey ,
PreviousVersion : writeResp3 . Entity . Version ,
} )
require . NoError ( t , err )
@ -317,53 +311,40 @@ func TestIntegrationEntityServer(t *testing.T) {
} )
t . Run ( "should be able to list objects" , func ( t * testing . T ) {
uid2 := "uid2"
uid3 := "uid3"
uid4 := "uid4"
kind2 := entity . StandardKindPlaylist
w1 , err := testCtx . client . Write ( ctx , & entity . WriteEntityRequest {
w1 , err := testCtx . client . Create ( ctx , & entity . CreateEntityRequest {
Entity : & entity . Entity {
GRN : testGrn ,
Key : testKey + "1" ,
Body : body ,
} ,
} )
require . NoError ( t , err )
w2 , err := testCtx . client . Wri te( ctx , & entity . Wri teEntityRequest{
w2 , err := testCtx . client . Crea te( ctx , & entity . Crea teEntityRequest{
Entity : & entity . Entity {
GRN : & grn . GRN {
ResourceIdentifier : uid2 ,
ResourceKind : kind ,
} ,
Key : testKey + "2" ,
Body : body ,
} ,
} )
require . NoError ( t , err )
w3 , err := testCtx . client . Wri te( ctx , & entity . Wri teEntityRequest{
w3 , err := testCtx . client . Crea te( ctx , & entity . Crea teEntityRequest{
Entity : & entity . Entity {
GRN : & grn . GRN {
ResourceIdentifier : uid3 ,
ResourceKind : kind2 ,
} ,
Key : testKey + "3" ,
Body : body ,
} ,
} )
require . NoError ( t , err )
w4 , err := testCtx . client . Wri te( ctx , & entity . Wri teEntityRequest{
w4 , err := testCtx . client . Crea te( ctx , & entity . Crea teEntityRequest{
Entity : & entity . Entity {
GRN : & grn . GRN {
ResourceIdentifier : uid4 ,
ResourceKind : kind2 ,
} ,
Key : testKey + "4" ,
Body : body ,
} ,
} )
require . NoError ( t , err )
resp , err := testCtx . client . List ( ctx , & entity . EntityListRequest {
Kind : [ ] string { kind , kind 2} ,
Resource : [ ] string { resource , resource 2} ,
WithBody : false ,
} )
require . NoError ( t , err )
@ -373,8 +354,8 @@ func TestIntegrationEntityServer(t *testing.T) {
kinds := make ( [ ] string , 0 , len ( resp . Results ) )
version := make ( [ ] string , 0 , len ( resp . Results ) )
for _ , res := range resp . Results {
uids = append ( uids , res . GRN . ResourceIdentifier )
kinds = append ( kinds , res . GRN . ResourceKind )
uids = append ( uids , res . Uid )
kinds = append ( kinds , res . Resource )
version = append ( version , res . Version )
}
require . Equal ( t , [ ] string { "my-test-entity" , "uid2" , "uid3" , "uid4" } , uids )
@ -388,15 +369,15 @@ func TestIntegrationEntityServer(t *testing.T) {
// Again with only one kind
respKind1 , err := testCtx . client . List ( ctx , & entity . EntityListRequest {
Kind : [ ] string { kind } ,
Resource : [ ] string { resource } ,
} )
require . NoError ( t , err )
uids = make ( [ ] string , 0 , len ( respKind1 . Results ) )
kinds = make ( [ ] string , 0 , len ( respKind1 . Results ) )
version = make ( [ ] string , 0 , len ( respKind1 . Results ) )
for _ , res := range respKind1 . Results {
uids = append ( uids , res . GRN . ResourceIdentifier )
kinds = append ( kinds , res . GRN . ResourceKind )
uids = append ( uids , res . Uid )
kinds = append ( kinds , res . Resource )
version = append ( version , res . Version )
}
require . Equal ( t , [ ] string { "my-test-entity" , "uid2" } , uids )
@ -409,32 +390,25 @@ func TestIntegrationEntityServer(t *testing.T) {
t . Run ( "should be able to filter objects based on their labels" , func ( t * testing . T ) {
kind := entity . StandardKindDashboard
_ , err := testCtx . client . Wri te( ctx , & entity . Wri teEntityRequest{
_ , err := testCtx . client . Crea te( ctx , & entity . Crea teEntityRequest{
Entity : & entity . Entity {
GRN : & grn . GRN {
ResourceKind : kind ,
ResourceIdentifier : "blue-green" ,
} ,
Key : "/grafana/dashboards/blue-green" ,
Body : [ ] byte ( dashboardWithTagsBlueGreen ) ,
} ,
} )
require . NoError ( t , err )
_ , err = testCtx . client . Wri te( ctx , & entity . Wri teEntityRequest{
_ , err = testCtx . client . Crea te( ctx , & entity . Crea teEntityRequest{
Entity : & entity . Entity {
GRN : & grn . GRN {
ResourceKind : kind ,
ResourceIdentifier : "red-green" ,
} ,
Key : "/grafana/dashboards/red-green" ,
Body : [ ] byte ( dashboardWithTagsRedGreen ) ,
} ,
} )
require . NoError ( t , err )
resp , err := testCtx . client . List ( ctx , & entity . EntityListRequest {
Kind : [ ] string { kind } ,
WithBody : false ,
WithLabels : true ,
Key : [ ] string { kind } ,
WithBody : false ,
Labels : map [ string ] string {
"red" : "" ,
} ,
@ -442,12 +416,11 @@ func TestIntegrationEntityServer(t *testing.T) {
require . NoError ( t , err )
require . NotNil ( t , resp )
require . Len ( t , resp . Results , 1 )
require . Equal ( t , resp . Results [ 0 ] . GRN . ResourceIdentifier , "red-green" )
require . Equal ( t , resp . Results [ 0 ] . Uid , "red-green" )
resp , err = testCtx . client . List ( ctx , & entity . EntityListRequest {
Kind : [ ] string { kind } ,
WithBody : false ,
WithLabels : true ,
Key : [ ] string { kind } ,
WithBody : false ,
Labels : map [ string ] string {
"red" : "" ,
"green" : "" ,
@ -456,12 +429,11 @@ func TestIntegrationEntityServer(t *testing.T) {
require . NoError ( t , err )
require . NotNil ( t , resp )
require . Len ( t , resp . Results , 1 )
require . Equal ( t , resp . Results [ 0 ] . GRN . ResourceIdentifier , "red-green" )
require . Equal ( t , resp . Results [ 0 ] . Uid , "red-green" )
resp , err = testCtx . client . List ( ctx , & entity . EntityListRequest {
Kind : [ ] string { kind } ,
WithBody : false ,
WithLabels : true ,
Key : [ ] string { kind } ,
WithBody : false ,
Labels : map [ string ] string {
"red" : "invalid" ,
} ,
@ -471,9 +443,8 @@ func TestIntegrationEntityServer(t *testing.T) {
require . Len ( t , resp . Results , 0 )
resp , err = testCtx . client . List ( ctx , & entity . EntityListRequest {
Kind : [ ] string { kind } ,
WithBody : false ,
WithLabels : true ,
Key : [ ] string { kind } ,
WithBody : false ,
Labels : map [ string ] string {
"green" : "" ,
} ,
@ -483,9 +454,8 @@ func TestIntegrationEntityServer(t *testing.T) {
require . Len ( t , resp . Results , 2 )
resp , err = testCtx . client . List ( ctx , & entity . EntityListRequest {
Kind : [ ] string { kind } ,
WithBody : false ,
WithLabels : true ,
Key : [ ] string { kind } ,
WithBody : false ,
Labels : map [ string ] string {
"yellow" : "" ,
} ,