ObjectStore: Replace path model with folder, uid, and slug model (#59452)

This commit is contained in:
Ryan McKinley 2022-11-30 12:10:35 -08:00 committed by GitHub
parent 5f3291be03
commit b4b843be66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 664 additions and 906 deletions

View File

@ -125,9 +125,8 @@ func (e *objectStoreJob) start(ctx context.Context) {
_, err = e.store.AdminWrite(ctx, &object.AdminWriteObjectRequest{
GRN: &object.GRN{
Scope: models.ObjectStoreScopeEntity,
UID: dash.UID,
Kind: models.StandardKindDashboard,
UID: dash.UID,
Kind: models.StandardKindDashboard,
},
ClearHistory: true,
Version: fmt.Sprintf("%d", dash.Version),
@ -135,9 +134,11 @@ func (e *objectStoreJob) start(ctx context.Context) {
UpdatedAt: dash.Updated.UnixMilli(),
UpdatedBy: fmt.Sprintf("user:%d", dash.UpdatedBy),
CreatedBy: fmt.Sprintf("user:%d", dash.CreatedBy),
Origin: "export-from-sql",
Body: dash.Data,
Comment: "(exported from SQL)",
Origin: &object.ObjectOriginInfo{
Source: "export-from-sql",
},
})
if err != nil {
e.status.Status = "error: " + err.Error()
@ -175,9 +176,8 @@ func (e *objectStoreJob) start(ctx context.Context) {
_, err = e.store.Write(ctx, &object.WriteObjectRequest{
GRN: &object.GRN{
Scope: models.ObjectStoreScopeEntity,
UID: playlist.Uid,
Kind: models.StandardKindPlaylist,
UID: playlist.Uid,
Kind: models.StandardKindPlaylist,
},
Body: prettyJSON(playlist),
Comment: "export from playlists",
@ -240,9 +240,8 @@ func (e *objectStoreJob) start(ctx context.Context) {
_, err = e.store.Write(ctx, &object.WriteObjectRequest{
GRN: &object.GRN{
Scope: models.ObjectStoreScopeEntity,
UID: dto.Key,
Kind: models.StandardKindSnapshot,
UID: dto.Key,
Kind: models.StandardKindSnapshot,
},
Body: prettyJSON(m),
Comment: "export from snapshtts",

View File

@ -60,7 +60,6 @@ func (s *objectStoreImpl) sync() {
TenantId: info.OrgID,
UID: info.UID,
Kind: models.StandardKindPlaylist,
Scope: models.ObjectStoreScopeEntity,
},
Body: body,
})
@ -76,9 +75,8 @@ func (s *objectStoreImpl) Create(ctx context.Context, cmd *playlist.CreatePlayli
}
_, err = s.objectstore.Write(ctx, &object.WriteObjectRequest{
GRN: &object.GRN{
Scope: models.ObjectStoreScopeEntity,
Kind: models.StandardKindPlaylist,
UID: rsp.UID,
Kind: models.StandardKindPlaylist,
UID: rsp.UID,
},
Body: body,
})
@ -98,9 +96,8 @@ func (s *objectStoreImpl) Update(ctx context.Context, cmd *playlist.UpdatePlayli
}
_, err = s.objectstore.Write(ctx, &object.WriteObjectRequest{
GRN: &object.GRN{
UID: rsp.Uid,
Kind: models.StandardKindPlaylist,
Scope: models.ObjectStoreScopeEntity,
UID: rsp.Uid,
Kind: models.StandardKindPlaylist,
},
Body: body,
})
@ -116,9 +113,8 @@ func (s *objectStoreImpl) Delete(ctx context.Context, cmd *playlist.DeletePlayli
if err == nil {
_, err = s.objectstore.Delete(ctx, &object.DeleteObjectRequest{
GRN: &object.GRN{
UID: cmd.UID,
Kind: models.StandardKindPlaylist,
Scope: models.ObjectStoreScopeEntity,
UID: cmd.UID,
Kind: models.StandardKindPlaylist,
},
})
if err != nil {
@ -148,9 +144,8 @@ func (s *objectStoreImpl) GetWithoutItems(ctx context.Context, q *playlist.GetPl
func (s *objectStoreImpl) Get(ctx context.Context, q *playlist.GetPlaylistByUidQuery) (*playlist.PlaylistDTO, error) {
rsp, err := s.objectstore.Read(ctx, &object.ReadObjectRequest{
GRN: &object.GRN{
UID: q.UID,
Kind: models.StandardKindPlaylist,
Scope: models.ObjectStoreScopeEntity,
UID: q.UID,
Kind: models.StandardKindPlaylist,
},
WithBody: true,
})

View File

@ -8,33 +8,34 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
func getKeyColumn(name string, isPrimaryKey bool) *migrator.Column {
func getLatinPathColumn(name string) *migrator.Column {
return &migrator.Column{
Name: name,
Type: migrator.DB_NVarchar,
Length: 1024,
Nullable: false,
IsPrimaryKey: isPrimaryKey,
IsLatin: true, // only used in MySQL
Name: name,
Type: migrator.DB_NVarchar,
Length: 1024,
Nullable: false,
IsLatin: true, // only used in MySQL
}
}
func addObjectStorageMigrations(mg *migrator.Migrator) {
grnLength := 256 // len(tenant)~8 + len(kind)!16 + len(kind)~128 = 256
tables := []migrator.Table{}
tables = append(tables, migrator.Table{
Name: "object",
Name: "entity",
Columns: []*migrator.Column{
// Object path contains everything required to make it unique across all instances
// orgId + scope + kind + uid
getKeyColumn("path", true),
// Object ID (OID) will be unique across all objects/instances
// uuid5( tenant_id, kind + uid )
{Name: "grn", Type: migrator.DB_NVarchar, Length: grnLength, Nullable: false, IsPrimaryKey: true},
// This is an optimization for listing everything at the same level in the object store
getKeyColumn("parent_folder_path", false),
// The object type
// The entity identifier
{Name: "tenant_id", Type: migrator.DB_BigInt, Nullable: false},
{Name: "kind", Type: migrator.DB_NVarchar, Length: 255, Nullable: false},
{Name: "uid", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
{Name: "folder", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
{Name: "slug", Type: migrator.DB_NVarchar, Length: 189, Nullable: false}, // from title
// The raw object body (any byte array)
// The raw entity body (any byte array)
{Name: "body", Type: migrator.DB_LongBlob, Nullable: false},
{Name: "size", Type: migrator.DB_BigInt, Nullable: false},
{Name: "etag", Type: migrator.DB_NVarchar, Length: 32, Nullable: false, IsLatin: true}, // md5(body)
@ -47,7 +48,8 @@ func addObjectStorageMigrations(mg *migrator.Migrator) {
{Name: "created_by", Type: migrator.DB_NVarchar, Length: 190, Nullable: false},
// Mark objects with origin metadata
{Name: "origin", Type: migrator.DB_Text, Nullable: true},
{Name: "origin", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
getLatinPathColumn("origin_key"), // index with length 1024
{Name: "origin_ts", Type: migrator.DB_BigInt, Nullable: false},
// Summary data (always extracted from the `body` column)
@ -58,28 +60,46 @@ func addObjectStorageMigrations(mg *migrator.Migrator) {
{Name: "errors", Type: migrator.DB_Text, Nullable: true}, // JSON object
},
Indices: []*migrator.Index{
{Cols: []string{"parent_folder_path"}}, // list in folder
{Cols: []string{"kind"}}, // filter by type
{Cols: []string{"kind"}},
{Cols: []string{"folder"}},
{Cols: []string{"uid"}},
{Cols: []string{"tenant_id", "kind", "uid"}, Type: migrator.UniqueIndex},
// {Cols: []string{"tenant_id", "folder", "slug"}, Type: migrator.UniqueIndex},
},
})
// when saving a folder, keep a path version cached
tables = append(tables, migrator.Table{
Name: "entity_folder",
Columns: []*migrator.Column{
{Name: "grn", Type: migrator.DB_NVarchar, Length: grnLength, Nullable: false},
getLatinPathColumn("path"), // slug/slug/slug/...
{Name: "depth", Type: migrator.DB_Int, Nullable: false},
{Name: "tree", Type: migrator.DB_Text, Nullable: false}, // JSON array from root
},
Indices: []*migrator.Index{
{Cols: []string{"path"}, Type: migrator.UniqueIndex},
},
})
tables = append(tables, migrator.Table{
Name: "object_labels",
Name: "entity_labels",
Columns: []*migrator.Column{
getKeyColumn("path", false),
{Name: "grn", Type: migrator.DB_NVarchar, Length: grnLength, Nullable: false},
{Name: "label", Type: migrator.DB_NVarchar, Length: 191, Nullable: false},
{Name: "value", Type: migrator.DB_NVarchar, Length: 1024, Nullable: false},
},
Indices: []*migrator.Index{
{Cols: []string{"path", "label"}, Type: migrator.UniqueIndex},
{Cols: []string{"grn", "label"}, Type: migrator.UniqueIndex},
},
})
tables = append(tables, migrator.Table{
Name: "object_ref",
Name: "entity_ref",
Columns: []*migrator.Column{
// Source:
getKeyColumn("path", false),
{Name: "grn", Type: migrator.DB_NVarchar, Length: grnLength, Nullable: false},
// Address (defined in the body, not resolved, may be invalid and change)
{Name: "kind", Type: migrator.DB_NVarchar, Length: 255, Nullable: false},
@ -88,21 +108,21 @@ func addObjectStorageMigrations(mg *migrator.Migrator) {
// Runtime calcs (will depend on the system state)
{Name: "resolved_ok", Type: migrator.DB_Bool, Nullable: false},
getKeyColumn("resolved_to", false),
{Name: "resolved_to", Type: migrator.DB_NVarchar, Length: 40, Nullable: false},
{Name: "resolved_warning", Type: migrator.DB_NVarchar, Length: 255, Nullable: false},
{Name: "resolved_time", Type: migrator.DB_DateTime, Nullable: false}, // resolution cache timestamp
},
Indices: []*migrator.Index{
{Cols: []string{"path"}, Type: migrator.IndexType},
{Cols: []string{"grn"}, Type: migrator.IndexType},
{Cols: []string{"kind"}, Type: migrator.IndexType},
{Cols: []string{"resolved_to"}, Type: migrator.IndexType},
},
})
tables = append(tables, migrator.Table{
Name: "object_history",
Name: "entity_history",
Columns: []*migrator.Column{
getKeyColumn("path", false),
{Name: "grn", Type: migrator.DB_NVarchar, Length: grnLength, Nullable: false},
{Name: "version", Type: migrator.DB_NVarchar, Length: 128, Nullable: false},
// Raw bytes
@ -118,7 +138,7 @@ func addObjectStorageMigrations(mg *migrator.Migrator) {
{Name: "message", Type: migrator.DB_Text, Nullable: false}, // defaults to empty string
},
Indices: []*migrator.Index{
{Cols: []string{"path", "version"}, Type: migrator.UniqueIndex},
{Cols: []string{"grn", "version"}, Type: migrator.UniqueIndex},
{Cols: []string{"updated_by"}, Type: migrator.IndexType},
},
})
@ -134,25 +154,28 @@ func addObjectStorageMigrations(mg *migrator.Migrator) {
// Migration cleanups: given that this is a complex setup
// that requires a lot of testing before we are ready to push out of dev
// this script lets us easy wipe previous changes and initialize clean tables
suffix := " (v5)" // change this when we want to wipe and reset the object tables
mg.AddMigration("ObjectStore init: cleanup"+suffix, migrator.NewRawSQLMigration(strings.TrimSpace(`
suffix := " (v8)" // change this when we want to wipe and reset the object tables
mg.AddMigration("EntityStore init: cleanup"+suffix, migrator.NewRawSQLMigration(strings.TrimSpace(`
DELETE FROM migration_log WHERE migration_id LIKE 'EntityStore init%';
`)))
// for a while this was called "ObjectStore"... this can be removed before we remove the dev only flags
mg.AddMigration("EntityStore init: object cleanup"+suffix, migrator.NewRawSQLMigration(strings.TrimSpace(`
DELETE FROM migration_log WHERE migration_id LIKE 'ObjectStore init%';
`)))
// Initialize all tables
for t := range tables {
mg.AddMigration("ObjectStore init: drop "+tables[t].Name+suffix, migrator.NewRawSQLMigration(
mg.AddMigration("EntityStore init: drop "+tables[t].Name+suffix, migrator.NewRawSQLMigration(
fmt.Sprintf("DROP TABLE IF EXISTS %s", tables[t].Name),
))
mg.AddMigration("ObjectStore init: table "+tables[t].Name+suffix, migrator.NewAddTableMigration(tables[t]))
mg.AddMigration("EntityStore init: table "+tables[t].Name+suffix, migrator.NewAddTableMigration(tables[t]))
for i := range tables[t].Indices {
mg.AddMigration(fmt.Sprintf("ObjectStore init: index %s[%d]"+suffix, tables[t].Name, i), migrator.NewAddIndexMigration(tables[t], tables[t].Indices[i]))
mg.AddMigration(fmt.Sprintf("EntityStore init: index %s[%d]"+suffix, tables[t].Name, i), migrator.NewAddIndexMigration(tables[t], tables[t].Indices[i]))
}
}
// TODO: add collation support to `migrator.Column`
mg.AddMigration("ObjectStore init: set path collation in object tables"+suffix, migrator.NewRawSQLMigration("").
mg.AddMigration("EntityStore init: set path collation in entity tables"+suffix, migrator.NewRawSQLMigration("").
// MySQL `utf8mb4_unicode_ci` collation is set in `mysql_dialect.go`
// SQLite uses a `BINARY` collation by default
Postgres("ALTER TABLE object ALTER COLUMN path TYPE VARCHAR(1024) COLLATE \"C\";")) // Collate C - sorting done based on character code byte values
Postgres("ALTER TABLE entity_folder ALTER COLUMN path TYPE VARCHAR(1024) COLLATE \"C\";")) // Collate C - sorting done based on character code byte values
}

View File

@ -1,5 +1,9 @@
package object
import (
"fmt"
)
// Check if the two GRNs reference to the same object
// we can not use simple `*x == *b` because of the internal settings
func (x *GRN) Equals(b *GRN) bool {
@ -7,9 +11,11 @@ func (x *GRN) Equals(b *GRN) bool {
return false
}
return x == b || (x.TenantId == b.TenantId &&
x.Scope == b.Scope &&
x.Kind == b.Kind &&
x.UID == b.UID)
}
// TODO: this should interpoerate with the GRN string flavor
// Set an OID based on the GRN
func (x *GRN) ToGRNString() string {
return fmt.Sprintf("grn:%d/%s/%s", x.TenantId, x.Kind, x.UID)
}

View File

@ -12,7 +12,6 @@ import (
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/services/store/kind"
"github.com/grafana/grafana/pkg/services/store/object"
"github.com/grafana/grafana/pkg/services/store/router"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/web"
@ -27,18 +26,16 @@ type HTTPObjectStore interface {
}
type httpObjectStore struct {
store object.ObjectStoreServer
log log.Logger
kinds kind.KindRegistry
router router.ObjectStoreRouter
store object.ObjectStoreServer
log log.Logger
kinds kind.KindRegistry
}
func ProvideHTTPObjectStore(store object.ObjectStoreServer, kinds kind.KindRegistry) HTTPObjectStore {
return &httpObjectStore{
store: store,
log: log.New("http-object-store"),
kinds: kinds,
router: router.NewObjectStoreRouter(kinds),
store: store,
log: log.New("http-object-store"),
kinds: kinds,
}
}
@ -48,16 +45,16 @@ func (s *httpObjectStore) RegisterHTTPRoutes(route routing.RouteRegister) {
reqGrafanaAdmin := middleware.ReqSignedIn //.ReqGrafanaAdmin
// Every * must parse to a GRN (uid+kind)
route.Get("/store/*", reqGrafanaAdmin, routing.Wrap(s.doGetObject))
route.Post("/store/*", reqGrafanaAdmin, routing.Wrap(s.doWriteObject))
route.Delete("/store/*", reqGrafanaAdmin, routing.Wrap(s.doDeleteObject))
route.Get("/raw/*", reqGrafanaAdmin, routing.Wrap(s.doGetRawObject))
route.Get("/history/*", reqGrafanaAdmin, routing.Wrap(s.doGetHistory))
route.Get("/list/*", reqGrafanaAdmin, routing.Wrap(s.doListFolder)) // Simplified version of search -- path is prefix
route.Get("/store/:kind/:uid", reqGrafanaAdmin, routing.Wrap(s.doGetObject))
route.Post("/store/:kind/:uid", reqGrafanaAdmin, routing.Wrap(s.doWriteObject))
route.Delete("/store/:kind/:uid", reqGrafanaAdmin, routing.Wrap(s.doDeleteObject))
route.Get("/raw/:kind/:uid", reqGrafanaAdmin, routing.Wrap(s.doGetRawObject))
route.Get("/history/:kind/:uid", reqGrafanaAdmin, routing.Wrap(s.doGetHistory))
route.Get("/list/:uid", reqGrafanaAdmin, routing.Wrap(s.doListFolder)) // Simplified version of search -- path is prefix
route.Get("/search", reqGrafanaAdmin, routing.Wrap(s.doSearch))
// File upload
route.Post("/upload/:scope", reqGrafanaAdmin, routing.Wrap(s.doUpload))
route.Post("/upload", reqGrafanaAdmin, routing.Wrap(s.doUpload))
}
// This function will extract UID+Kind from the requested path "*" in our router
@ -65,12 +62,6 @@ func (s *httpObjectStore) RegisterHTTPRoutes(route routing.RouteRegister) {
// This will quickly be revisited as we explore how to encode UID+Kind in a "GRN" format
func (s *httpObjectStore) getGRNFromRequest(c *models.ReqContext) (*object.GRN, map[string]string, error) {
params := web.Params(c.Req)
info, err := s.router.RouteFromKey(c.Req.Context(),
fmt.Sprintf("%d/%s", c.OrgID, params["*"]))
if err != nil {
return nil, params, err
}
// Read parameters that are encoded in the URL
vals := c.Req.URL.Query()
for k, v := range vals {
@ -78,7 +69,11 @@ func (s *httpObjectStore) getGRNFromRequest(c *models.ReqContext) (*object.GRN,
params[k] = v[0]
}
}
return info.GRN, params, nil
return &object.GRN{
TenantId: c.OrgID,
Kind: params[":kind"],
UID: params[":uid"],
}, params, nil
}
func (s *httpObjectStore) doGetObject(c *models.ReqContext) response.Response {
@ -182,6 +177,7 @@ func (s *httpObjectStore) doWriteObject(c *models.ReqContext) response.Response
rsp, err := s.store.Write(c.Req.Context(), &object.WriteObjectRequest{
GRN: grn,
Body: b,
Folder: params["folder"],
Comment: params["comment"],
PreviousVersion: params["previousVersion"],
})
@ -233,10 +229,6 @@ func (s *httpObjectStore) doUpload(c *models.ReqContext) response.Response {
if len(fileinfo) < 1 {
return response.Error(400, "missing files", nil)
}
scope := web.Params(c.Req)[":scope"]
if scope == "" {
return response.Error(400, "invalid scope", nil)
}
var rsp []*object.WriteObjectResponse
@ -257,12 +249,7 @@ func (s *httpObjectStore) doUpload(c *models.ReqContext) response.Response {
if err != nil || kind.ID == "" {
return response.Error(400, "Unsupported kind: "+fileHeader.Filename, err)
}
uid := folder
if uid != "" {
uid = uid + "/" + fileHeader.Filename[:idx]
} else {
uid = fileHeader.Filename[:idx]
}
uid := fileHeader.Filename[:idx]
file, err := fileHeader.Open()
if err != nil {
@ -278,9 +265,9 @@ func (s *httpObjectStore) doUpload(c *models.ReqContext) response.Response {
}
grn := &object.GRN{
Scope: scope,
UID: uid,
Kind: kind.ID,
UID: uid,
Kind: kind.ID,
TenantId: c.OrgID,
}
if !overwriteExistingFile {
@ -301,6 +288,7 @@ func (s *httpObjectStore) doUpload(c *models.ReqContext) response.Response {
GRN: grn,
Body: data,
Comment: message,
Folder: folder,
// PreviousVersion: params["previousVersion"],
})

View File

@ -96,6 +96,16 @@ func (codec *rawObjectCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream)
stream.WriteObjectField("etag")
stream.WriteString(obj.ETag)
}
if obj.Folder != "" {
stream.WriteMore()
stream.WriteObjectField("folder")
stream.WriteString(obj.Folder)
}
if obj.Slug != "" {
stream.WriteMore()
stream.WriteObjectField("slug")
stream.WriteString(obj.Slug)
}
if obj.Size > 0 {
stream.WriteMore()
stream.WriteObjectField("size")
@ -135,6 +145,10 @@ func readRawObject(iter *jsoniter.Iterator, raw *RawObject) {
raw.Size = iter.ReadInt64()
case "etag":
raw.ETag = iter.ReadString()
case "folder":
raw.Folder = iter.ReadString()
case "slug":
raw.Slug = iter.ReadString()
case "version":
raw.Version = iter.ReadString()
case "origin":

View File

@ -80,15 +80,11 @@ type GRN struct {
// the tenant/org id
TenantId int64 `protobuf:"varint,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
// mabybe "namespace"? valid values include
// * entity
// * drive
// * service/xyz
Scope string `protobuf:"bytes,2,opt,name=scope,proto3" json:"scope,omitempty"`
// Identify the object kind. This kind will be used to apply a schema to the body and
// will trigger additional indexing behavior.
Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"`
// Unique ID
// less then 40, no slashes or other special characters
UID string `protobuf:"bytes,4,opt,name=UID,proto3" json:"UID,omitempty"`
}
@ -131,13 +127,6 @@ func (x *GRN) GetTenantId() int64 {
return 0
}
func (x *GRN) GetScope() string {
if x != nil {
return x.Scope
}
return ""
}
func (x *GRN) GetKind() string {
if x != nil {
return x.Kind
@ -174,12 +163,16 @@ type RawObject struct {
ETag string `protobuf:"bytes,7,opt,name=ETag,proto3" json:"ETag,omitempty"`
// Raw bytes of the storage object. The kind will determine what is a valid payload
Body []byte `protobuf:"bytes,8,opt,name=body,proto3" json:"body,omitempty"`
// Folder UID
Folder string `protobuf:"bytes,9,opt,name=folder,proto3" json:"folder,omitempty"`
// Unique slug within folder (may be UID)
Slug string `protobuf:"bytes,10,opt,name=slug,proto3" json:"slug,omitempty"`
// The version will change when the object is saved. It is not necessarily sortable
//
// NOTE: currently managed by the dashboard+dashboard_version tables
Version string `protobuf:"bytes,9,opt,name=version,proto3" json:"version,omitempty"`
Version string `protobuf:"bytes,11,opt,name=version,proto3" json:"version,omitempty"`
// External location info
Origin *ObjectOriginInfo `protobuf:"bytes,10,opt,name=origin,proto3" json:"origin,omitempty"`
Origin *ObjectOriginInfo `protobuf:"bytes,12,opt,name=origin,proto3" json:"origin,omitempty"`
}
func (x *RawObject) Reset() {
@ -270,6 +263,20 @@ func (x *RawObject) GetBody() []byte {
return nil
}
func (x *RawObject) GetFolder() string {
if x != nil {
return x.Folder
}
return ""
}
func (x *RawObject) GetSlug() string {
if x != nil {
return x.Slug
}
return ""
}
func (x *RawObject) GetVersion() string {
if x != nil {
return x.Version
@ -291,8 +298,10 @@ type ObjectOriginInfo struct {
// NOTE: currently managed by the dashboard_provisioning table
Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"`
// Key in the upstream system
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
// Time in epoch milliseconds that the object was last synced with an external system (provisioning/git)
Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"`
Time int64 `protobuf:"varint,3,opt,name=time,proto3" json:"time,omitempty"`
}
func (x *ObjectOriginInfo) Reset() {
@ -334,6 +343,13 @@ func (x *ObjectOriginInfo) GetSource() string {
return ""
}
func (x *ObjectOriginInfo) GetKey() string {
if x != nil {
return x.Key
}
return ""
}
func (x *ObjectOriginInfo) GetTime() int64 {
if x != nil {
return x.Time
@ -738,12 +754,14 @@ type WriteObjectRequest struct {
// Object identifier
GRN *GRN `protobuf:"bytes,1,opt,name=GRN,proto3" json:"GRN,omitempty"`
// Where to save the object (empty will leave it unchanged)
Folder string `protobuf:"bytes,2,opt,name=folder,proto3" json:"folder,omitempty"`
// The raw object body
Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
// Message that can be seen when exploring object history
Comment string `protobuf:"bytes,3,opt,name=comment,proto3" json:"comment,omitempty"`
Comment string `protobuf:"bytes,4,opt,name=comment,proto3" json:"comment,omitempty"`
// Used for optimistic locking. If missing, the previous version will be replaced regardless
PreviousVersion string `protobuf:"bytes,4,opt,name=previous_version,json=previousVersion,proto3" json:"previous_version,omitempty"`
PreviousVersion string `protobuf:"bytes,5,opt,name=previous_version,json=previousVersion,proto3" json:"previous_version,omitempty"`
}
func (x *WriteObjectRequest) Reset() {
@ -785,6 +803,13 @@ func (x *WriteObjectRequest) GetGRN() *GRN {
return nil
}
func (x *WriteObjectRequest) GetFolder() string {
if x != nil {
return x.Folder
}
return ""
}
func (x *WriteObjectRequest) GetBody() []byte {
if x != nil {
return x.Body
@ -816,33 +841,35 @@ type AdminWriteObjectRequest struct {
// Object identifier
GRN *GRN `protobuf:"bytes,1,opt,name=GRN,proto3" json:"GRN,omitempty"`
// Where to save the object (empty will leave it unchanged)
Folder string `protobuf:"bytes,2,opt,name=folder,proto3" json:"folder,omitempty"`
// The raw object body
Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
Body []byte `protobuf:"bytes,3,opt,name=body,proto3" json:"body,omitempty"`
// Message that can be seen when exploring object history
Comment string `protobuf:"bytes,3,opt,name=comment,proto3" json:"comment,omitempty"`
Comment string `protobuf:"bytes,4,opt,name=comment,proto3" json:"comment,omitempty"`
// Time in epoch milliseconds that the object was created
// Optional, if 0 it will use the current time
CreatedAt int64 `protobuf:"varint,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
CreatedAt int64 `protobuf:"varint,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
// Time in epoch milliseconds that the object was updated
// Optional, if empty it will use the current user
UpdatedAt int64 `protobuf:"varint,5,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
UpdatedAt int64 `protobuf:"varint,6,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"`
// Who created the object
// Optional, if 0 it will use the current time
CreatedBy string `protobuf:"bytes,6,opt,name=created_by,json=createdBy,proto3" json:"created_by,omitempty"`
CreatedBy string `protobuf:"bytes,7,opt,name=created_by,json=createdBy,proto3" json:"created_by,omitempty"`
// Who updated the object
// Optional, if empty it will use the current user
UpdatedBy string `protobuf:"bytes,7,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"`
UpdatedBy string `protobuf:"bytes,8,opt,name=updated_by,json=updatedBy,proto3" json:"updated_by,omitempty"`
// An explicit version identifier
// Optional, if set, this will overwrite/define an explicit version
Version string `protobuf:"bytes,8,opt,name=version,proto3" json:"version,omitempty"`
Version string `protobuf:"bytes,9,opt,name=version,proto3" json:"version,omitempty"`
// Used for optimistic locking. If missing, the previous version will be replaced regardless
// This may not be used along with an explicit version in the request
PreviousVersion string `protobuf:"bytes,9,opt,name=previous_version,json=previousVersion,proto3" json:"previous_version,omitempty"`
PreviousVersion string `protobuf:"bytes,10,opt,name=previous_version,json=previousVersion,proto3" json:"previous_version,omitempty"`
// Request that all previous versions are removed from the history
// This will make sense for systems that manage history explicitly externallay
ClearHistory bool `protobuf:"varint,10,opt,name=clear_history,json=clearHistory,proto3" json:"clear_history,omitempty"`
ClearHistory bool `protobuf:"varint,11,opt,name=clear_history,json=clearHistory,proto3" json:"clear_history,omitempty"`
// Optionally define where the object came from
Origin string `protobuf:"bytes,11,opt,name=origin,proto3" json:"origin,omitempty"`
Origin *ObjectOriginInfo `protobuf:"bytes,12,opt,name=origin,proto3" json:"origin,omitempty"`
}
func (x *AdminWriteObjectRequest) Reset() {
@ -884,6 +911,13 @@ func (x *AdminWriteObjectRequest) GetGRN() *GRN {
return nil
}
func (x *AdminWriteObjectRequest) GetFolder() string {
if x != nil {
return x.Folder
}
return ""
}
func (x *AdminWriteObjectRequest) GetBody() []byte {
if x != nil {
return x.Body
@ -947,11 +981,11 @@ func (x *AdminWriteObjectRequest) GetClearHistory() bool {
return false
}
func (x *AdminWriteObjectRequest) GetOrigin() string {
func (x *AdminWriteObjectRequest) GetOrigin() *ObjectOriginInfo {
if x != nil {
return x.Origin
}
return ""
return nil
}
type WriteObjectResponse struct {
@ -1427,10 +1461,14 @@ type ObjectSearchResult struct {
Description string `protobuf:"bytes,8,opt,name=description,proto3" json:"description,omitempty"`
// The structured labels
Labels map[string]string `protobuf:"bytes,9,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// Folder UID
Folder string `protobuf:"bytes,10,opt,name=folder,proto3" json:"folder,omitempty"`
// Slugified name
Slug string `protobuf:"bytes,11,opt,name=slug,proto3" json:"slug,omitempty"`
// Optionally include extracted JSON
FieldsJson []byte `protobuf:"bytes,10,opt,name=fields_json,json=fieldsJson,proto3" json:"fields_json,omitempty"`
FieldsJson []byte `protobuf:"bytes,12,opt,name=fields_json,json=fieldsJson,proto3" json:"fields_json,omitempty"`
// ObjectErrorInfo in json
ErrorJson []byte `protobuf:"bytes,11,opt,name=error_json,json=errorJson,proto3" json:"error_json,omitempty"`
ErrorJson []byte `protobuf:"bytes,13,opt,name=error_json,json=errorJson,proto3" json:"error_json,omitempty"`
}
func (x *ObjectSearchResult) Reset() {
@ -1528,6 +1566,20 @@ func (x *ObjectSearchResult) GetLabels() map[string]string {
return nil
}
func (x *ObjectSearchResult) GetFolder() string {
if x != nil {
return x.Folder
}
return ""
}
func (x *ObjectSearchResult) GetSlug() string {
if x != nil {
return x.Slug
}
return ""
}
func (x *ObjectSearchResult) GetFieldsJson() []byte {
if x != nil {
return x.FieldsJson
@ -1602,252 +1654,261 @@ var File_object_proto protoreflect.FileDescriptor
var file_object_proto_rawDesc = []byte{
0x0a, 0x0c, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x5e, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x1b, 0x0a,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x48, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x1b, 0x0a,
0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63,
0x6f, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65,
0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6b, 0x69, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x55, 0x49, 0x44, 0x22, 0xae, 0x02, 0x0a, 0x09, 0x52, 0x61, 0x77, 0x4f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x52, 0x4e, 0x52, 0x03,
0x47, 0x52, 0x4e, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74,
0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41,
0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18,
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79,
0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x05,
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12,
0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73,
0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x45, 0x54, 0x61, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x45, 0x54, 0x61, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18,
0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18,
0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52,
0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x22, 0x3e, 0x0a, 0x10, 0x4f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x73,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x22, 0x62, 0x0a, 0x0f, 0x4f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f,
0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18,
0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x74, 0x61,
0x69, 0x6c, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b,
0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0xad, 0x01, 0x0a, 0x11,
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66,
0x6f, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70,
0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69,
0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x10,
0x0a, 0x03, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x49, 0x44,
0x22, 0xda, 0x02, 0x0a, 0x09, 0x52, 0x61, 0x77, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d,
0x0a, 0x03, 0x47, 0x52, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x1d, 0x0a,
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70,
0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a,
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a,
0x04, 0x45, 0x54, 0x61, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x45, 0x54, 0x61,
0x67, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x11,
0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e,
0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x77,
0x69, 0x74, 0x68, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x69, 0x74, 0x68, 0x5f,
0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x77,
0x69, 0x74, 0x68, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x22, 0x62, 0x0a, 0x12, 0x52, 0x65,
0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x29, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x11, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x52, 0x61, 0x77, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73,
0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x0b, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0x49,
0x0a, 0x16, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63,
0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x2e, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x22, 0x4f, 0x0a, 0x17, 0x42, 0x61, 0x74,
0x63, 0x68, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18,
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x52,
0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x8c, 0x01, 0x0a, 0x12, 0x57,
0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e,
0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04,
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a,
0x04, 0x45, 0x54, 0x61, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x45, 0x54, 0x61,
0x67, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18,
0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a,
0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75,
0x67, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x06, 0x6f,
0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4f, 0x72, 0x69, 0x67, 0x69,
0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x22, 0x50, 0x0a,
0x10, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66,
0x6f, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74,
0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x22,
0x62, 0x0a, 0x0f, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x49, 0x6e,
0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x4a,
0x73, 0x6f, 0x6e, 0x22, 0xad, 0x01, 0x0a, 0x11, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x42,
0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52,
0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x45, 0x54, 0x61, 0x67, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x45, 0x54, 0x61, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d,
0x6d, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d,
0x65, 0x6e, 0x74, 0x22, 0x8c, 0x01, 0x0a, 0x11, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18,
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x77, 0x69, 0x74, 0x68, 0x42, 0x6f, 0x64, 0x79, 0x12,
0x21, 0x0a, 0x0c, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18,
0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x77, 0x69, 0x74, 0x68, 0x53, 0x75, 0x6d, 0x6d, 0x61,
0x72, 0x79, 0x22, 0x62, 0x0a, 0x12, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x2e, 0x52, 0x61, 0x77, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x06, 0x6f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x6a,
0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x75, 0x6d, 0x6d, 0x61,
0x72, 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0x49, 0x0a, 0x16, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52,
0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x2f, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x19, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63,
0x68, 0x22, 0x4f, 0x0a, 0x17, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x07,
0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c,
0x74, 0x73, 0x22, 0xa4, 0x01, 0x0a, 0x12, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64,
0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72,
0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04,
0x62, 0x6f, 0x64, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x29,
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x29,
0x0a, 0x10, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f,
0x75, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xe4, 0x02, 0x0a, 0x17, 0x41, 0x64,
0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f,
0x75, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x96, 0x03, 0x0a, 0x17, 0x41, 0x64,
0x6d, 0x69, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x52, 0x4e, 0x52,
0x03, 0x47, 0x52, 0x4e, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d,
0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65,
0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74,
0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41,
0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18,
0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74,
0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x06,
0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12,
0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x07, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x18,
0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x65, 0x76,
0x69, 0x6f, 0x75, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x5f, 0x68, 0x69, 0x73,
0x74, 0x6f, 0x72, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x63, 0x6c, 0x65, 0x61,
0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67,
0x69, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e,
0x22, 0xb3, 0x02, 0x0a, 0x13, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f,
0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f,
0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x52,
0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x31, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66,
0x6f, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x75, 0x6d,
0x6d, 0x61, 0x72, 0x79, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x0b, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x06,
0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x3c, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12, 0x0b, 0x0a,
0x07, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x50,
0x44, 0x41, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43, 0x48, 0x41,
0x4e, 0x47, 0x45, 0x44, 0x10, 0x03, 0x22, 0x5f, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a,
0x03, 0x47, 0x52, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x2e, 0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x29, 0x0a, 0x10,
0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x26, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x0e, 0x0a, 0x02, 0x4f, 0x4b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x4f, 0x4b, 0x22,
0x73, 0x0a, 0x14, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x52,
0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18,
0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x26, 0x0a, 0x0f,
0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18,
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x95, 0x01, 0x0a, 0x15, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48,
0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d,
0x0a, 0x03, 0x47, 0x52, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x35, 0x0a,
0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x19, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67,
0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e,
0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x84, 0x03, 0x0a,
0x13, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67,
0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e,
0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05,
0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d,
0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64,
0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06,
0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f,
0x6c, 0x64, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x06,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c,
0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x07, 0x20,
0x03, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x69, 0x74,
0x68, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x77, 0x69,
0x74, 0x68, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6c,
0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x77, 0x69, 0x74,
0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x69, 0x74, 0x68, 0x5f,
0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x77, 0x69,
0x74, 0x68, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65,
0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
0x02, 0x38, 0x01, 0x22, 0xa4, 0x03, 0x0a, 0x12, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65,
0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52,
0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x2e, 0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74,
0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
0x64, 0x5f, 0x62, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61,
0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x06, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a,
0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12,
0x3e, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x26, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53,
0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65,
0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12,
0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x4a, 0x73, 0x6f, 0x6e,
0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0b,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4a, 0x73, 0x6f, 0x6e, 0x1a,
0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x74, 0x0a, 0x14, 0x4f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x34, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52,
0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74,
0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x32, 0xfa, 0x03, 0x0a, 0x0b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65,
0x12, 0x3d, 0x0a, 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x19, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x61,
0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x4c, 0x0a, 0x09, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x64, 0x4f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x64, 0x4f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a,
0x05, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x57, 0x72, 0x69, 0x74,
0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x43, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x07, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12,
0x1c, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48,
0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x69, 0x73,
0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x06,
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x1b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x03, 0x47, 0x52, 0x4e, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04,
0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79,
0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72,
0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72,
0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74,
0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x12, 0x29, 0x0a, 0x10, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76,
0x69, 0x6f, 0x75, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63,
0x6c, 0x65, 0x61, 0x72, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x0b, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0c, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79,
0x12, 0x30, 0x0a, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x18, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x6f, 0x72, 0x69, 0x67,
0x69, 0x6e, 0x22, 0xb3, 0x02, 0x0a, 0x13, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x49, 0x6e,
0x66, 0x6f, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x31, 0x0a, 0x06, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49,
0x6e, 0x66, 0x6f, 0x52, 0x06, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x73,
0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x0b, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x3a,
0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x3c, 0x0a, 0x06, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x00, 0x12,
0x0b, 0x0a, 0x07, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07,
0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x43,
0x48, 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, 0x03, 0x22, 0x5f, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x29,
0x0a, 0x10, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f,
0x75, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x26, 0x0a, 0x14, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x0e, 0x0a, 0x02, 0x4f, 0x4b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x4f,
0x4b, 0x22, 0x73, 0x0a, 0x14, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f,
0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69,
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x26,
0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65,
0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67,
0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x95, 0x01, 0x0a, 0x15, 0x4f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x1d, 0x0a, 0x03, 0x47, 0x52, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12,
0x35, 0x0a, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x19, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70,
0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x84,
0x03, 0x0a, 0x13, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70,
0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14,
0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c,
0x69, 0x6d, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69,
0x6e, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x16,
0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73,
0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x4a, 0x0a, 0x0a, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12,
0x1f, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x57, 0x72,
0x65, 0x73, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18,
0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x77,
0x69, 0x74, 0x68, 0x5f, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,
0x77, 0x69, 0x74, 0x68, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x69, 0x74, 0x68,
0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x77,
0x69, 0x74, 0x68, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x69, 0x74,
0x68, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
0x77, 0x69, 0x74, 0x68, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61,
0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd0, 0x03, 0x0a, 0x12, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1d, 0x0a, 0x03,
0x47, 0x52, 0x4e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x2e, 0x47, 0x52, 0x4e, 0x52, 0x03, 0x47, 0x52, 0x4e, 0x12, 0x18, 0x0a, 0x07, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20,
0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70,
0x64, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18,
0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x3e, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x26, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x4c, 0x61,
0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c,
0x73, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75,
0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x1f, 0x0a,
0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x4a, 0x73, 0x6f, 0x6e, 0x12, 0x1d,
0x0a, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4a, 0x73, 0x6f, 0x6e, 0x1a, 0x39, 0x0a,
0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x74, 0x0a, 0x14, 0x4f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x34, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72,
0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70,
0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0d, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0xfa,
0x03, 0x0a, 0x0b, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x3d,
0x0a, 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x19, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e,
0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1a, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x4f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a,
0x09, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x64, 0x12, 0x1e, 0x2e, 0x6f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x2e, 0x42, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x05, 0x57,
0x72, 0x69, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x57, 0x72,
0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x5e, 0x0a,
0x10, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x41, 0x64, 0x6d, 0x69,
0x6e, 0x12, 0x4a, 0x0a, 0x0a, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12,
0x1f, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x57, 0x72,
0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, 0x5a,
0x09, 0x2e, 0x2f, 0x3b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a,
0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74,
0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x46, 0x0a, 0x07, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x1c, 0x2e,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x69, 0x73,
0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x48, 0x69, 0x73, 0x74, 0x6f,
0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x06, 0x53, 0x65,
0x61, 0x72, 0x63, 0x68, 0x12, 0x1b, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62,
0x6a, 0x65, 0x63, 0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1c, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x4f, 0x62, 0x6a, 0x65, 0x63,
0x74, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x4a, 0x0a, 0x0a, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x1f, 0x2e,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x57, 0x72, 0x69, 0x74,
0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x5e, 0x0a, 0x10, 0x4f,
0x62, 0x6a, 0x65, 0x63, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12,
0x4a, 0x0a, 0x0a, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x1f, 0x2e,
0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x57, 0x72, 0x69, 0x74,
0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b,
0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4f, 0x62, 0x6a,
0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0b, 0x5a, 0x09, 0x2e,
0x2f, 0x3b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -1897,39 +1958,40 @@ var file_object_proto_depIdxs = []int32{
7, // 5: object.BatchReadObjectResponse.results:type_name -> object.ReadObjectResponse
1, // 6: object.WriteObjectRequest.GRN:type_name -> object.GRN
1, // 7: object.AdminWriteObjectRequest.GRN:type_name -> object.GRN
4, // 8: object.WriteObjectResponse.error:type_name -> object.ObjectErrorInfo
1, // 9: object.WriteObjectResponse.GRN:type_name -> object.GRN
5, // 10: object.WriteObjectResponse.object:type_name -> object.ObjectVersionInfo
0, // 11: object.WriteObjectResponse.status:type_name -> object.WriteObjectResponse.Status
1, // 12: object.DeleteObjectRequest.GRN:type_name -> object.GRN
1, // 13: object.ObjectHistoryRequest.GRN:type_name -> object.GRN
1, // 14: object.ObjectHistoryResponse.GRN:type_name -> object.GRN
5, // 15: object.ObjectHistoryResponse.versions:type_name -> object.ObjectVersionInfo
20, // 16: object.ObjectSearchRequest.labels:type_name -> object.ObjectSearchRequest.LabelsEntry
1, // 17: object.ObjectSearchResult.GRN:type_name -> object.GRN
21, // 18: object.ObjectSearchResult.labels:type_name -> object.ObjectSearchResult.LabelsEntry
18, // 19: object.ObjectSearchResponse.results:type_name -> object.ObjectSearchResult
6, // 20: object.ObjectStore.Read:input_type -> object.ReadObjectRequest
8, // 21: object.ObjectStore.BatchRead:input_type -> object.BatchReadObjectRequest
10, // 22: object.ObjectStore.Write:input_type -> object.WriteObjectRequest
13, // 23: object.ObjectStore.Delete:input_type -> object.DeleteObjectRequest
15, // 24: object.ObjectStore.History:input_type -> object.ObjectHistoryRequest
17, // 25: object.ObjectStore.Search:input_type -> object.ObjectSearchRequest
11, // 26: object.ObjectStore.AdminWrite:input_type -> object.AdminWriteObjectRequest
11, // 27: object.ObjectStoreAdmin.AdminWrite:input_type -> object.AdminWriteObjectRequest
7, // 28: object.ObjectStore.Read:output_type -> object.ReadObjectResponse
9, // 29: object.ObjectStore.BatchRead:output_type -> object.BatchReadObjectResponse
12, // 30: object.ObjectStore.Write:output_type -> object.WriteObjectResponse
14, // 31: object.ObjectStore.Delete:output_type -> object.DeleteObjectResponse
16, // 32: object.ObjectStore.History:output_type -> object.ObjectHistoryResponse
19, // 33: object.ObjectStore.Search:output_type -> object.ObjectSearchResponse
12, // 34: object.ObjectStore.AdminWrite:output_type -> object.WriteObjectResponse
12, // 35: object.ObjectStoreAdmin.AdminWrite:output_type -> object.WriteObjectResponse
28, // [28:36] is the sub-list for method output_type
20, // [20:28] is the sub-list for method input_type
20, // [20:20] is the sub-list for extension type_name
20, // [20:20] is the sub-list for extension extendee
0, // [0:20] is the sub-list for field type_name
3, // 8: object.AdminWriteObjectRequest.origin:type_name -> object.ObjectOriginInfo
4, // 9: object.WriteObjectResponse.error:type_name -> object.ObjectErrorInfo
1, // 10: object.WriteObjectResponse.GRN:type_name -> object.GRN
5, // 11: object.WriteObjectResponse.object:type_name -> object.ObjectVersionInfo
0, // 12: object.WriteObjectResponse.status:type_name -> object.WriteObjectResponse.Status
1, // 13: object.DeleteObjectRequest.GRN:type_name -> object.GRN
1, // 14: object.ObjectHistoryRequest.GRN:type_name -> object.GRN
1, // 15: object.ObjectHistoryResponse.GRN:type_name -> object.GRN
5, // 16: object.ObjectHistoryResponse.versions:type_name -> object.ObjectVersionInfo
20, // 17: object.ObjectSearchRequest.labels:type_name -> object.ObjectSearchRequest.LabelsEntry
1, // 18: object.ObjectSearchResult.GRN:type_name -> object.GRN
21, // 19: object.ObjectSearchResult.labels:type_name -> object.ObjectSearchResult.LabelsEntry
18, // 20: object.ObjectSearchResponse.results:type_name -> object.ObjectSearchResult
6, // 21: object.ObjectStore.Read:input_type -> object.ReadObjectRequest
8, // 22: object.ObjectStore.BatchRead:input_type -> object.BatchReadObjectRequest
10, // 23: object.ObjectStore.Write:input_type -> object.WriteObjectRequest
13, // 24: object.ObjectStore.Delete:input_type -> object.DeleteObjectRequest
15, // 25: object.ObjectStore.History:input_type -> object.ObjectHistoryRequest
17, // 26: object.ObjectStore.Search:input_type -> object.ObjectSearchRequest
11, // 27: object.ObjectStore.AdminWrite:input_type -> object.AdminWriteObjectRequest
11, // 28: object.ObjectStoreAdmin.AdminWrite:input_type -> object.AdminWriteObjectRequest
7, // 29: object.ObjectStore.Read:output_type -> object.ReadObjectResponse
9, // 30: object.ObjectStore.BatchRead:output_type -> object.BatchReadObjectResponse
12, // 31: object.ObjectStore.Write:output_type -> object.WriteObjectResponse
14, // 32: object.ObjectStore.Delete:output_type -> object.DeleteObjectResponse
16, // 33: object.ObjectStore.History:output_type -> object.ObjectHistoryResponse
19, // 34: object.ObjectStore.Search:output_type -> object.ObjectSearchResponse
12, // 35: object.ObjectStore.AdminWrite:output_type -> object.WriteObjectResponse
12, // 36: object.ObjectStoreAdmin.AdminWrite:output_type -> object.WriteObjectResponse
29, // [29:37] is the sub-list for method output_type
21, // [21:29] is the sub-list for method input_type
21, // [21:21] is the sub-list for extension type_name
21, // [21:21] is the sub-list for extension extendee
0, // [0:21] is the sub-list for field type_name
}
func init() { file_object_proto_init() }

View File

@ -7,17 +7,12 @@ message GRN {
// the tenant/org id
int64 tenant_id = 1;
// mabybe "namespace"? valid values include
// * entity
// * drive
// * service/xyz
string scope = 2;
// Identify the object kind. This kind will be used to apply a schema to the body and
// will trigger additional indexing behavior.
string kind = 3;
// Unique ID
// 40 characters or less, no slashes or other special characters
string UID = 4;
}
@ -47,21 +42,30 @@ message RawObject {
// Raw bytes of the storage object. The kind will determine what is a valid payload
bytes body = 8;
// Folder UID
string folder = 9;
// Unique slug within folder (may be UID)
string slug = 10;
// The version will change when the object is saved. It is not necessarily sortable
//
// NOTE: currently managed by the dashboard+dashboard_version tables
string version = 9;
string version = 11;
// External location info
ObjectOriginInfo origin = 10;
ObjectOriginInfo origin = 12;
}
message ObjectOriginInfo {
// NOTE: currently managed by the dashboard_provisioning table
string source = 1;
// Key in the upstream system
string key = 2;
// Time in epoch milliseconds that the object was last synced with an external system (provisioning/git)
int64 time = 2;
int64 time = 3;
}
// Report error while working with objects
@ -146,14 +150,17 @@ message WriteObjectRequest {
// Object identifier
GRN GRN = 1;
// Where to save the object (empty will leave it unchanged)
string folder = 2;
// The raw object body
bytes body = 2;
bytes body = 3;
// Message that can be seen when exploring object history
string comment = 3;
string comment = 4;
// Used for optimistic locking. If missing, the previous version will be replaced regardless
string previous_version = 4;
string previous_version = 5;
}
// This operation is useful when syncing a resource from external sources
@ -163,42 +170,45 @@ message AdminWriteObjectRequest {
// Object identifier
GRN GRN = 1;
// Where to save the object (empty will leave it unchanged)
string folder = 2;
// The raw object body
bytes body = 2;
bytes body = 3;
// Message that can be seen when exploring object history
string comment = 3;
string comment = 4;
// Time in epoch milliseconds that the object was created
// Optional, if 0 it will use the current time
int64 created_at = 4;
int64 created_at = 5;
// Time in epoch milliseconds that the object was updated
// Optional, if empty it will use the current user
int64 updated_at = 5;
int64 updated_at = 6;
// Who created the object
// Optional, if 0 it will use the current time
string created_by = 6;
string created_by = 7;
// Who updated the object
// Optional, if empty it will use the current user
string updated_by = 7;
string updated_by = 8;
// An explicit version identifier
// Optional, if set, this will overwrite/define an explicit version
string version = 8;
string version = 9;
// Used for optimistic locking. If missing, the previous version will be replaced regardless
// This may not be used along with an explicit version in the request
string previous_version = 9;
string previous_version = 10;
// Request that all previous versions are removed from the history
// This will make sense for systems that manage history explicitly externallay
bool clear_history = 10;
bool clear_history = 11;
// Optionally define where the object came from
string origin = 11;
ObjectOriginInfo origin = 12;
}
message WriteObjectResponse {
@ -338,11 +348,17 @@ message ObjectSearchResult {
// The structured labels
map<string,string> labels = 9;
// Folder UID
string folder = 10;
// Slugified name
string slug = 11;
// Optionally include extracted JSON
bytes fields_json = 10;
bytes fields_json = 12;
// ObjectErrorInfo in json
bytes error_json = 11;
bytes error_json = 13;
}
message ObjectSearchResponse {

View File

@ -12,7 +12,7 @@ type selectQuery struct {
args []interface{}
}
func (q *selectQuery) addWhere(f string, val string) {
func (q *selectQuery) addWhere(f string, val interface{}) {
q.args = append(q.args, val)
q.where = append(q.where, f+"=?")
}
@ -37,11 +37,6 @@ func (q *selectQuery) addWhereIn(f string, vals []string) {
}
}
func (q *selectQuery) addWherePrefix(f string, v string) {
q.args = append(q.args, v+"%")
q.where = append(q.where, f+" LIKE ?")
}
func (q *selectQuery) toQuery() (string, []interface{}) {
args := q.args
sb := strings.Builder{}

View File

@ -11,15 +11,12 @@ import (
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/grpcserver"
"github.com/grafana/grafana/pkg/services/sqlstore/session"
"github.com/grafana/grafana/pkg/services/store"
"github.com/grafana/grafana/pkg/services/store/kind"
"github.com/grafana/grafana/pkg/services/store/kind/folder"
"github.com/grafana/grafana/pkg/services/store/object"
"github.com/grafana/grafana/pkg/services/store/resolver"
"github.com/grafana/grafana/pkg/services/store/router"
"github.com/grafana/grafana/pkg/setting"
)
@ -33,7 +30,6 @@ func ProvideSQLObjectServer(db db.DB, cfg *setting.Cfg, grpcServerProvider grpcs
log: log.New("sql-object-server"),
kinds: kinds,
resolver: resolver,
router: router.NewObjectStoreRouter(kinds),
}
object.RegisterObjectStoreServer(grpcServerProvider.GetServer(), objectServer)
return objectServer
@ -44,16 +40,16 @@ type sqlObjectServer struct {
sess *session.SessionDB
kinds kind.KindRegistry
resolver resolver.ObjectReferenceResolver
router router.ObjectStoreRouter
}
func getReadSelect(r *object.ReadObjectRequest) string {
fields := []string{
"path", "kind", "version",
"tenant_id", "kind", "uid", // The PK
"version", "slug", "folder",
"size", "etag", "errors", // errors are always returned
"created_at", "created_by",
"updated_at", "updated_by",
"origin", "origin_ts"}
"origin", "origin_key", "origin_ts"}
if r.WithBody {
fields = append(fields, `body`)
@ -61,24 +57,24 @@ func getReadSelect(r *object.ReadObjectRequest) string {
if r.WithSummary {
fields = append(fields, `name`, `description`, `labels`, `fields`)
}
return "SELECT " + strings.Join(fields, ",") + " FROM object WHERE "
return "SELECT " + strings.Join(fields, ",") + " FROM entity WHERE "
}
func (s *sqlObjectServer) rowToReadObjectResponse(ctx context.Context, rows *sql.Rows, r *object.ReadObjectRequest) (*object.ReadObjectResponse, error) {
path := "" // string (extract UID?)
var origin sql.NullString
originTime := int64(0)
raw := &object.RawObject{
GRN: &object.GRN{},
GRN: &object.GRN{},
Origin: &object.ObjectOriginInfo{},
}
slug := ""
summaryjson := &summarySupport{}
args := []interface{}{
&path, &raw.GRN.Kind, &raw.Version,
&raw.GRN.TenantId, &raw.GRN.Kind, &raw.GRN.UID,
&raw.Version, &slug, &raw.Folder,
&raw.Size, &raw.ETag, &summaryjson.errors,
&raw.CreatedAt, &raw.CreatedBy,
&raw.UpdatedAt, &raw.UpdatedBy,
&origin, &originTime,
&raw.Origin.Source, &raw.Origin.Key, &raw.Origin.Time,
}
if r.WithBody {
args = append(args, &raw.Body)
@ -92,17 +88,8 @@ func (s *sqlObjectServer) rowToReadObjectResponse(ctx context.Context, rows *sql
return nil, err
}
if origin.Valid {
raw.Origin = &object.ObjectOriginInfo{
Source: origin.String,
Time: originTime,
}
}
// Get the GRN from key. TODO? save each part as a column?
info, _ := s.router.RouteFromKey(ctx, path)
if info.GRN != nil {
raw.GRN = info.GRN
if raw.Origin.Source == "" {
raw.Origin = nil
}
rsp := &object.ReadObjectResponse{
@ -124,35 +111,43 @@ func (s *sqlObjectServer) rowToReadObjectResponse(ctx context.Context, rows *sql
return rsp, nil
}
func (s *sqlObjectServer) getObjectKey(ctx context.Context, grn *object.GRN) (router.ResourceRouteInfo, error) {
func (s *sqlObjectServer) validateGRN(ctx context.Context, grn *object.GRN) (*object.GRN, error) {
if grn == nil {
return router.ResourceRouteInfo{}, fmt.Errorf("missing grn")
return nil, fmt.Errorf("missing GRN")
}
user := store.UserFromContext(ctx)
if user == nil {
return router.ResourceRouteInfo{}, fmt.Errorf("can not find user in context")
}
if user.OrgID != grn.TenantId {
if grn.TenantId > 0 {
return router.ResourceRouteInfo{}, fmt.Errorf("invalid user (wrong tenant id)")
}
if grn.TenantId == 0 {
grn.TenantId = user.OrgID
} else if grn.TenantId != user.OrgID {
return nil, fmt.Errorf("tenant ID does not match userID")
}
return s.router.Route(ctx, grn)
if grn.Kind == "" {
return nil, fmt.Errorf("GRN missing kind")
}
if grn.UID == "" {
return nil, fmt.Errorf("GRN missing UID")
}
if len(grn.UID) > 40 {
return nil, fmt.Errorf("GRN UID is too long (>40)")
}
if strings.ContainsAny(grn.UID, "/#$@?") {
return nil, fmt.Errorf("invalid character in GRN")
}
return grn, nil
}
func (s *sqlObjectServer) Read(ctx context.Context, r *object.ReadObjectRequest) (*object.ReadObjectResponse, error) {
if r.Version != "" {
return s.readFromHistory(ctx, r)
}
route, err := s.getObjectKey(ctx, r.GRN)
grn, err := s.validateGRN(ctx, r.GRN)
if err != nil {
return nil, err
}
args := []interface{}{route.Key}
where := "path=?"
args := []interface{}{grn.ToGRNString()}
where := "grn=?"
rows, err := s.sess.Query(ctx, getReadSelect(r)+where, args...)
if err != nil {
@ -168,10 +163,11 @@ func (s *sqlObjectServer) Read(ctx context.Context, r *object.ReadObjectRequest)
}
func (s *sqlObjectServer) readFromHistory(ctx context.Context, r *object.ReadObjectRequest) (*object.ReadObjectResponse, error) {
route, err := s.getObjectKey(ctx, r.GRN)
grn, err := s.validateGRN(ctx, r.GRN)
if err != nil {
return nil, err
}
oid := grn.ToGRNString()
fields := []string{
"body", "size", "etag",
@ -180,7 +176,7 @@ func (s *sqlObjectServer) readFromHistory(ctx context.Context, r *object.ReadObj
rows, err := s.sess.Query(ctx,
"SELECT "+strings.Join(fields, ",")+
" FROM object_history WHERE path=? AND version=?", route.Key, r.Version)
" FROM entity_history WHERE grn=? AND version=?", oid, r.Version)
if err != nil {
return nil, err
}
@ -243,13 +239,13 @@ func (s *sqlObjectServer) BatchRead(ctx context.Context, b *object.BatchReadObje
return nil, fmt.Errorf("requests must want the same things")
}
route, err := s.getObjectKey(ctx, r.GRN)
grn, err := s.validateGRN(ctx, r.GRN)
if err != nil {
return nil, err
}
where := "path=?"
args = append(args, route.Key)
where := "grn=?"
args = append(args, grn.ToGRNString())
if r.Version != "" {
return nil, fmt.Errorf("version not supported for batch read (yet?)")
}
@ -282,14 +278,11 @@ func (s *sqlObjectServer) Write(ctx context.Context, r *object.WriteObjectReques
//nolint:gocyclo
func (s *sqlObjectServer) AdminWrite(ctx context.Context, r *object.AdminWriteObjectRequest) (*object.WriteObjectResponse, error) {
route, err := s.getObjectKey(ctx, r.GRN)
grn, err := s.validateGRN(ctx, r.GRN)
if err != nil {
return nil, err
}
grn := route.GRN
if grn == nil {
return nil, fmt.Errorf("invalid grn")
}
oid := grn.ToGRNString()
timestamp := time.Now().UnixMilli()
createdAt := r.CreatedAt
@ -312,20 +305,15 @@ func (s *sqlObjectServer) AdminWrite(ctx context.Context, r *object.AdminWriteOb
return nil, err
}
slug := slugifyTitle(summary.name, r.GRN.UID)
etag := createContentsHash(body)
path := route.Key
rsp := &object.WriteObjectResponse{
GRN: grn,
Status: object.WriteObjectResponse_CREATED, // Will be changed if not true
}
// Make sure all parent folders exist
if grn.Scope == models.ObjectStoreScopeDrive {
err = s.ensureFolders(ctx, grn)
if err != nil {
return nil, err
}
origin := r.Origin
if origin == nil {
origin = &object.ObjectOriginInfo{}
}
err = s.sess.WithTransaction(ctx, func(tx *session.SessionTx) error {
@ -334,18 +322,18 @@ func (s *sqlObjectServer) AdminWrite(ctx context.Context, r *object.AdminWriteOb
if r.ClearHistory {
// Optionally keep the original creation time information
if createdAt < 1000 || createdBy == "" {
err = s.fillCreationInfo(ctx, tx, path, &createdAt, &createdBy)
err = s.fillCreationInfo(ctx, tx, oid, &createdAt, &createdBy)
if err != nil {
return err
}
}
_, err = doDelete(ctx, tx, path)
_, err = doDelete(ctx, tx, oid)
if err != nil {
return err
}
versionInfo = &object.ObjectVersionInfo{}
} else {
versionInfo, err = s.selectForUpdate(ctx, tx, path)
versionInfo, err = s.selectForUpdate(ctx, tx, oid)
if err != nil {
return err
}
@ -385,25 +373,25 @@ func (s *sqlObjectServer) AdminWrite(ctx context.Context, r *object.AdminWriteOb
if isUpdate {
// Clear the labels+refs
if _, err := tx.Exec(ctx, "DELETE FROM object_labels WHERE path=?", path); err != nil {
if _, err := tx.Exec(ctx, "DELETE FROM entity_labels WHERE grn=?", oid); err != nil {
return err
}
if _, err := tx.Exec(ctx, "DELETE FROM object_ref WHERE path=?", path); err != nil {
if _, err := tx.Exec(ctx, "DELETE FROM entity_ref WHERE grn=?", oid); err != nil {
return err
}
}
// 1. Add the `object_history` values
// 1. Add the `entity_history` values
versionInfo.Size = int64(len(body))
versionInfo.ETag = etag
versionInfo.UpdatedAt = updatedAt
versionInfo.UpdatedBy = updatedBy
_, err = tx.Exec(ctx, `INSERT INTO object_history (`+
"path, version, message, "+
_, err = tx.Exec(ctx, `INSERT INTO entity_history (`+
"grn, version, message, "+
"size, body, etag, "+
"updated_at, updated_by) "+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
path, versionInfo.Version, versionInfo.Comment,
oid, versionInfo.Version, versionInfo.Comment,
versionInfo.Size, body, versionInfo.ETag,
updatedAt, versionInfo.UpdatedBy,
)
@ -414,10 +402,10 @@ func (s *sqlObjectServer) AdminWrite(ctx context.Context, r *object.AdminWriteOb
// 2. Add the labels rows
for k, v := range summary.model.Labels {
_, err = tx.Exec(ctx,
`INSERT INTO object_labels `+
"(path, label, value) "+
`INSERT INTO entity_labels `+
"(grn, label, value) "+
`VALUES (?, ?, ?)`,
path, k, v,
oid, k, v,
)
if err != nil {
return err
@ -430,11 +418,11 @@ func (s *sqlObjectServer) AdminWrite(ctx context.Context, r *object.AdminWriteOb
if err != nil {
return err
}
_, err = tx.Exec(ctx, `INSERT INTO object_ref (`+
"path, kind, type, uid, "+
_, err = tx.Exec(ctx, `INSERT INTO entity_ref (`+
"grn, kind, type, uid, "+
"resolved_ok, resolved_to, resolved_warning, resolved_time) "+
`VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
path, ref.Kind, ref.Type, ref.UID,
oid, ref.Kind, ref.Type, ref.UID,
resolved.OK, resolved.Key, resolved.Warning, resolved.Timestamp,
)
if err != nil {
@ -446,19 +434,19 @@ func (s *sqlObjectServer) AdminWrite(ctx context.Context, r *object.AdminWriteOb
rsp.Object = versionInfo
if isUpdate {
rsp.Status = object.WriteObjectResponse_UPDATED
_, err = tx.Exec(ctx, "UPDATE object SET "+
_, err = tx.Exec(ctx, "UPDATE entity SET "+
"body=?, size=?, etag=?, version=?, "+
"updated_at=?, updated_by=?,"+
"name=?, description=?,"+
"labels=?, fields=?, errors=?, "+
"origin=?, origin_ts=? "+
"WHERE path=?",
"origin=?, origin_key=?, origin_ts=? "+
"WHERE grn=?",
body, versionInfo.Size, etag, versionInfo.Version,
updatedAt, versionInfo.UpdatedBy,
summary.model.Name, summary.model.Description,
summary.labels, summary.fields, summary.errors,
r.Origin, timestamp,
path,
origin.Source, origin.Key, timestamp,
oid,
)
return err
}
@ -470,16 +458,25 @@ func (s *sqlObjectServer) AdminWrite(ctx context.Context, r *object.AdminWriteOb
createdBy = updatedBy
}
_, err = tx.Exec(ctx, "INSERT INTO object ("+
"path, parent_folder_path, kind, size, body, etag, version, "+
_, err = tx.Exec(ctx, "INSERT INTO entity ("+
"grn, tenant_id, kind, uid, folder, "+
"size, body, etag, version, "+
"updated_at, updated_by, created_at, created_by, "+
"name, description, origin, origin_ts, "+
"labels, fields, errors) "+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
path, getParentFolderPath(grn.Kind, path), grn.Kind, versionInfo.Size, body, etag, versionInfo.Version,
"name, description, slug, "+
"labels, fields, errors, "+
"origin, origin_key, origin_ts) "+
"VALUES (?, ?, ?, ?, ?, "+
" ?, ?, ?, ?, "+
" ?, ?, ?, ?, "+
" ?, ?, ?, "+
" ?, ?, ?, "+
" ?, ?, ?)",
oid, grn.TenantId, grn.Kind, grn.UID, r.Folder,
versionInfo.Size, body, etag, versionInfo.Version,
updatedAt, createdBy, createdAt, createdBy, // created + updated are the same
summary.model.Name, summary.model.Description, r.Origin, timestamp,
summary.model.Name, summary.model.Description, slug,
summary.labels, summary.fields, summary.errors,
origin.Source, origin.Key, origin.Time,
)
return err
})
@ -490,7 +487,7 @@ func (s *sqlObjectServer) AdminWrite(ctx context.Context, r *object.AdminWriteOb
return rsp, err
}
func (s *sqlObjectServer) fillCreationInfo(ctx context.Context, tx *session.SessionTx, path string, createdAt *int64, createdBy *string) error {
func (s *sqlObjectServer) fillCreationInfo(ctx context.Context, tx *session.SessionTx, grn string, createdAt *int64, createdBy *string) error {
if *createdAt > 1000 {
ignore := int64(0)
createdAt = &ignore
@ -500,7 +497,7 @@ func (s *sqlObjectServer) fillCreationInfo(ctx context.Context, tx *session.Sess
createdBy = &ignore
}
rows, err := tx.Query(ctx, "SELECT created_at,created_by FROM object WHERE path=?", path)
rows, err := tx.Query(ctx, "SELECT created_at,created_by FROM entity WHERE grn=?", grn)
if err == nil {
if rows.Next() {
err = rows.Scan(&createdAt, &createdBy)
@ -512,12 +509,12 @@ func (s *sqlObjectServer) fillCreationInfo(ctx context.Context, tx *session.Sess
return err
}
func (s *sqlObjectServer) selectForUpdate(ctx context.Context, tx *session.SessionTx, path string) (*object.ObjectVersionInfo, error) {
q := "SELECT etag,version,updated_at,size FROM object WHERE path=?"
func (s *sqlObjectServer) selectForUpdate(ctx context.Context, tx *session.SessionTx, grn string) (*object.ObjectVersionInfo, error) {
q := "SELECT etag,version,updated_at,size FROM entity WHERE grn=?"
if false { // TODO, MYSQL/PosgreSQL can lock the row " FOR UPDATE"
q += " FOR UPDATE"
}
rows, err := tx.Query(ctx, q, path)
rows, err := tx.Query(ctx, q, grn)
if err != nil {
return nil, err
}
@ -551,22 +548,21 @@ func (s *sqlObjectServer) prepare(ctx context.Context, r *object.AdminWriteObjec
}
func (s *sqlObjectServer) Delete(ctx context.Context, r *object.DeleteObjectRequest) (*object.DeleteObjectResponse, error) {
route, err := s.getObjectKey(ctx, r.GRN)
grn, err := s.validateGRN(ctx, r.GRN)
if err != nil {
return nil, err
}
path := route.Key
rsp := &object.DeleteObjectResponse{}
err = s.sess.WithTransaction(ctx, func(tx *session.SessionTx) error {
rsp.OK, err = doDelete(ctx, tx, path)
rsp.OK, err = doDelete(ctx, tx, grn.ToGRNString())
return err
})
return rsp, err
}
func doDelete(ctx context.Context, tx *session.SessionTx, path string) (bool, error) {
results, err := tx.Exec(ctx, "DELETE FROM object WHERE path=?", path)
func doDelete(ctx context.Context, tx *session.SessionTx, grn string) (bool, error) {
results, err := tx.Exec(ctx, "DELETE FROM entity WHERE grn=?", grn)
if err != nil {
return false, err
}
@ -576,21 +572,21 @@ func doDelete(ctx context.Context, tx *session.SessionTx, path string) (bool, er
}
// TODO: keep history? would need current version bump, and the "write" would have to get from history
_, _ = tx.Exec(ctx, "DELETE FROM object_history WHERE path=?", path)
_, _ = tx.Exec(ctx, "DELETE FROM object_labels WHERE path=?", path)
_, _ = tx.Exec(ctx, "DELETE FROM object_ref WHERE path=?", path)
_, _ = tx.Exec(ctx, "DELETE FROM entity_history WHERE grn=?", grn)
_, _ = tx.Exec(ctx, "DELETE FROM entity_labels WHERE grn=?", grn)
_, _ = tx.Exec(ctx, "DELETE FROM entity_ref WHERE grn=?", grn)
return rows > 0, err
}
func (s *sqlObjectServer) History(ctx context.Context, r *object.ObjectHistoryRequest) (*object.ObjectHistoryResponse, error) {
route, err := s.getObjectKey(ctx, r.GRN)
grn, err := s.validateGRN(ctx, r.GRN)
if err != nil {
return nil, err
}
path := route.Key
oid := grn.ToGRNString()
page := ""
args := []interface{}{path}
args := []interface{}{oid}
if r.NextPageToken != "" {
// args = append(args, r.NextPageToken) // TODO, need to get time from the version
// page = "AND updated <= ?"
@ -598,8 +594,8 @@ func (s *sqlObjectServer) History(ctx context.Context, r *object.ObjectHistoryRe
}
query := "SELECT version,size,etag,updated_at,updated_by,message \n" +
" FROM object_history \n" +
" WHERE path=? " + page + "\n" +
" FROM entity_history \n" +
" WHERE grn=? " + page + "\n" +
" ORDER BY updated_at DESC LIMIT 100"
rows, err := s.sess.Query(ctx, query, args...)
@ -608,7 +604,7 @@ func (s *sqlObjectServer) History(ctx context.Context, r *object.ObjectHistoryRe
}
defer func() { _ = rows.Close() }()
rsp := &object.ObjectHistoryResponse{
GRN: route.GRN,
GRN: r.GRN,
}
for rows.Next() {
v := &object.ObjectVersionInfo{}
@ -632,7 +628,8 @@ func (s *sqlObjectServer) Search(ctx context.Context, r *object.ObjectSearchRequ
}
fields := []string{
"path", "kind", "version", "errors", // errors are always returned
"grn", "tenant_id", "kind", "uid",
"version", "folder", "slug", "errors", // errors are always returned
"size", "updated_at", "updated_by",
"name", "description", // basic summary
}
@ -649,31 +646,20 @@ func (s *sqlObjectServer) Search(ctx context.Context, r *object.ObjectSearchRequ
selectQuery := selectQuery{
fields: fields,
from: "object", // the table
from: "entity", // the table
args: []interface{}{},
limit: int(r.Limit),
oneExtra: true, // request one more than the limit (and show next token if it exists)
}
selectQuery.addWhere("tenant_id", user.OrgID)
if len(r.Kind) > 0 {
selectQuery.addWhereIn("kind", r.Kind)
}
// Locked to a folder or prefix
// Folder UID or OID?
if r.Folder != "" {
if strings.HasSuffix(r.Folder, "/") {
return nil, fmt.Errorf("folder should not end with slash")
}
if strings.HasSuffix(r.Folder, "*") {
keyPrefix := fmt.Sprintf("%d/%s", user.OrgID, strings.ReplaceAll(r.Folder, "*", ""))
selectQuery.addWherePrefix("path", keyPrefix)
} else {
keyPrefix := fmt.Sprintf("%d/%s", user.OrgID, r.Folder)
selectQuery.addWhere("parent_folder_path", keyPrefix)
}
} else {
keyPrefix := fmt.Sprintf("%d/", user.OrgID)
selectQuery.addWherePrefix("path", keyPrefix)
selectQuery.addWhere("folder", r.Folder)
}
query, args := selectQuery.toQuery()
@ -688,7 +674,7 @@ func (s *sqlObjectServer) Search(ctx context.Context, r *object.ObjectSearchRequ
return nil, err
}
defer func() { _ = rows.Close() }()
key := ""
oid := ""
rsp := &object.ObjectSearchResponse{}
for rows.Next() {
result := &object.ObjectSearchResult{
@ -697,7 +683,8 @@ func (s *sqlObjectServer) Search(ctx context.Context, r *object.ObjectSearchRequ
summaryjson := summarySupport{}
args := []interface{}{
&key, &result.GRN.Kind, &result.Version, &summaryjson.errors,
&oid, &result.GRN.TenantId, &result.GRN.Kind, &result.GRN.UID,
&result.Version, &result.Folder, &result.Slug, &summaryjson.errors,
&result.Size, &result.UpdatedAt, &result.UpdatedBy,
&result.Name, &summaryjson.description,
}
@ -716,16 +703,10 @@ func (s *sqlObjectServer) Search(ctx context.Context, r *object.ObjectSearchRequ
return rsp, err
}
info, err := s.router.RouteFromKey(ctx, key)
if err != nil {
return rsp, err
}
result.GRN = info.GRN
// found one more than requested
if len(rsp.Results) >= selectQuery.limit {
// TODO? should this encode start+offset?
rsp.NextPageToken = key
rsp.NextPageToken = oid
break
}
@ -753,54 +734,3 @@ func (s *sqlObjectServer) Search(ctx context.Context, r *object.ObjectSearchRequ
}
return rsp, err
}
func (s *sqlObjectServer) ensureFolders(ctx context.Context, objectgrn *object.GRN) error {
uid := objectgrn.UID
idx := strings.LastIndex(uid, "/")
var missing []*object.GRN
for idx > 0 {
parent := uid[:idx]
grn := &object.GRN{
TenantId: objectgrn.TenantId,
Scope: objectgrn.Scope,
Kind: models.StandardKindFolder,
UID: parent,
}
fr, err := s.router.Route(ctx, grn)
if err != nil {
return err
}
// Not super efficient, but maybe it is OK?
results := []int64{}
err = s.sess.Select(ctx, &results, "SELECT 1 from object WHERE path=?", fr.Key)
if err != nil {
return err
}
if len(results) == 0 {
missing = append([]*object.GRN{grn}, missing...)
}
idx = strings.LastIndex(parent, "/")
}
// walk though each missing element
for _, grn := range missing {
f := &folder.Model{
Name: store.GuessNameFromUID(grn.UID),
}
fmt.Printf("CREATE Folder: %s\n", grn.UID)
body, err := json.Marshal(f)
if err != nil {
return err
}
_, err = s.Write(ctx, &object.WriteObjectRequest{
GRN: grn,
Body: body,
})
if err != nil {
return err
}
}
return nil
}

View File

@ -2,10 +2,11 @@ package sqlstash
import (
"crypto/md5"
"encoding/base64"
"encoding/hex"
"strings"
"github.com/grafana/grafana/pkg/models"
"github.com/gosimple/slug"
)
func createContentsHash(contents []byte) string {
@ -13,15 +14,20 @@ func createContentsHash(contents []byte) string {
return hex.EncodeToString(hash[:])
}
func getParentFolderPath(kind string, key string) string {
idx := strings.LastIndex(key, "/")
if idx < 0 {
return "" // ?
func slugifyTitle(title string, fallback string) string {
if title == "" {
title = fallback
}
// folder should have a parent up one directory
if kind == models.StandardKindFolder {
idx = strings.LastIndex(key[:idx], "/")
s := slug.Make(strings.ToLower(title))
if s == "" {
// If the dashboard name is only characters outside of the
// sluggable characters, the slug creation will return an
// empty string which will mess up URLs. This failsafe picks
// that up and creates the slug as a base64 identifier instead.
s = base64.RawURLEncoding.EncodeToString([]byte(title))
if slug.MaxLength != 0 && len(s) > slug.MaxLength {
s = s[:slug.MaxLength]
}
}
return key[:idx]
return s
}

View File

@ -48,9 +48,6 @@ func requireObjectMatch(t *testing.T, obj *object.RawObject, m rawObjectMatcher)
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.Scope != "" && m.grn.Scope != obj.GRN.Scope {
mismatches += fmt.Sprintf("expected Scope: %s, actual: %s\n", m.grn.Scope, obj.GRN.Scope)
}
if m.grn.Kind != "" && m.grn.Kind != obj.GRN.Kind {
mismatches += fmt.Sprintf("expected Kind: %s, actual: %s\n", m.grn.Kind, obj.GRN.Kind)
}
@ -125,9 +122,8 @@ func TestIntegrationObjectServer(t *testing.T) {
firstVersion := "1"
kind := models.StandardKindJSONObj
grn := &object.GRN{
Kind: kind,
UID: "my-test-entity",
Scope: models.ObjectStoreScopeEntity,
Kind: kind,
UID: "my-test-entity",
}
body := []byte("{\"name\":\"John\"}")
@ -171,7 +167,6 @@ func TestIntegrationObjectServer(t *testing.T) {
foundGRN := readResp.Object.GRN
require.NotNil(t, foundGRN)
require.Equal(t, testCtx.user.OrgID, foundGRN.TenantId) // orgId becomes the tenant id when not set
require.Equal(t, grn.Scope, foundGRN.Scope)
require.Equal(t, grn.Kind, foundGRN.Kind)
require.Equal(t, grn.UID, foundGRN.UID)
@ -205,9 +200,8 @@ func TestIntegrationObjectServer(t *testing.T) {
t.Run("should be able to update an object", func(t *testing.T) {
before := time.Now()
grn := &object.GRN{
Kind: kind,
UID: util.GenerateShortUID(),
Scope: models.ObjectStoreScopeEntity,
Kind: kind,
UID: util.GenerateShortUID(),
}
writeReq1 := &object.WriteObjectRequest{
@ -316,9 +310,8 @@ func TestIntegrationObjectServer(t *testing.T) {
w2, err := testCtx.client.Write(ctx, &object.WriteObjectRequest{
GRN: &object.GRN{
UID: uid2,
Kind: kind,
Scope: grn.Scope,
UID: uid2,
Kind: kind,
},
Body: body,
})
@ -326,9 +319,8 @@ func TestIntegrationObjectServer(t *testing.T) {
w3, err := testCtx.client.Write(ctx, &object.WriteObjectRequest{
GRN: &object.GRN{
UID: uid3,
Kind: kind2,
Scope: grn.Scope,
UID: uid3,
Kind: kind2,
},
Body: body,
})
@ -336,9 +328,8 @@ func TestIntegrationObjectServer(t *testing.T) {
w4, err := testCtx.client.Write(ctx, &object.WriteObjectRequest{
GRN: &object.GRN{
UID: uid4,
Kind: kind2,
Scope: grn.Scope,
UID: uid4,
Kind: kind2,
},
Body: body,
})

View File

@ -5,6 +5,7 @@ func ToAdminWriteObjectRequest(req *WriteObjectRequest) *AdminWriteObjectRequest
return &AdminWriteObjectRequest{
GRN: req.GRN,
Body: req.Body,
Folder: req.Folder,
Comment: req.Comment,
PreviousVersion: req.PreviousVersion,
}

View File

@ -1,173 +0,0 @@
package router
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/store/kind"
"github.com/grafana/grafana/pkg/services/store/object"
)
type ResourceRouteInfo struct {
// The resource identifier
GRN *object.GRN
// Raw key used in storage engine
Key string
}
type ObjectStoreRouter interface {
// This will throw exceptions for unsupported
Route(ctx context.Context, grn *object.GRN) (ResourceRouteInfo, error)
// Parse a key to get the GRN and storage information
RouteFromKey(ctx context.Context, key string) (ResourceRouteInfo, error)
}
type standardStoreRouter struct {
kinds kind.KindRegistry
}
func NewObjectStoreRouter(kinds kind.KindRegistry) ObjectStoreRouter {
return &standardStoreRouter{kinds: kinds}
}
var _ ObjectStoreRouter = &standardStoreRouter{}
func (r *standardStoreRouter) Route(ctx context.Context, grn *object.GRN) (ResourceRouteInfo, error) {
info := ResourceRouteInfo{
GRN: grn,
}
if grn == nil {
return info, fmt.Errorf("missing GRN")
}
// Make sure the orgID is set
if grn.TenantId < 1 {
return info, fmt.Errorf("missing TenantId")
}
if grn.Kind == "" {
return info, fmt.Errorf("missing Kind")
}
if grn.UID == "" {
return info, fmt.Errorf("missing UID")
}
kind, err := r.kinds.GetInfo(grn.Kind)
if err != nil {
return info, fmt.Errorf("unknown Kind: " + grn.Kind)
}
if grn.Scope == "" {
return info, fmt.Errorf("missing Scope")
}
switch grn.Scope {
case models.ObjectStoreScopeEntity:
{
info.Key = fmt.Sprintf("%d/%s/%s/%s", grn.TenantId, grn.Scope, grn.Kind, grn.UID)
}
case models.ObjectStoreScopeDrive:
{
// Special folder handling in drive
if grn.Kind == models.StandardKindFolder {
info.Key = fmt.Sprintf("%d/%s/%s/__folder.json", grn.TenantId, grn.Scope, grn.UID)
return info, nil
}
if kind.FileExtension != "" {
info.Key = fmt.Sprintf("%d/%s/%s.%s", grn.TenantId, grn.Scope, grn.UID, kind.FileExtension)
} else {
info.Key = fmt.Sprintf("%d/%s/%s-%s.json", grn.TenantId, grn.Scope, grn.UID, grn.Kind)
}
}
default:
return info, fmt.Errorf("unsupported scope: " + grn.Scope)
}
return info, nil
}
func (r *standardStoreRouter) RouteFromKey(ctx context.Context, key string) (ResourceRouteInfo, error) {
info := ResourceRouteInfo{
Key: key,
GRN: &object.GRN{},
}
// {orgID}/{scope}/....
idx := strings.Index(key, "/")
if idx <= 0 {
return info, fmt.Errorf("can not find orgID")
}
p0 := key[:idx]
key = key[idx+1:]
idx = strings.Index(key, "/")
if idx <= 0 {
return info, fmt.Errorf("can not find namespace")
}
p2 := key[:idx]
key = key[idx+1:]
tenantID, err := strconv.ParseInt(p0, 10, 64)
if err != nil {
return info, fmt.Errorf("error parsing orgID")
}
info.GRN.TenantId = tenantID
info.GRN.Scope = p2
switch info.GRN.Scope {
case models.ObjectStoreScopeDrive:
{
idx := strings.LastIndex(key, ".")
if idx > 0 {
ext := key[idx+1:]
if ext == "json" {
sdx := strings.LastIndex(key, "/")
idx = strings.LastIndex(key, "-")
if idx > sdx {
ddx := strings.LastIndex(key, ".") // .json
info.GRN.UID = key[:idx]
info.GRN.Kind = key[idx+1 : ddx]
} else {
switch key[sdx+1:] {
case "__folder.json":
{
info.GRN.UID = key[:sdx]
info.GRN.Kind = models.StandardKindFolder
}
default:
return info, fmt.Errorf("unable to parse drive path")
}
}
} else {
info.GRN.UID = key[:idx]
k, err := r.kinds.GetFromExtension(ext)
if err != nil {
return info, err
}
info.GRN.Kind = k.ID
}
} else {
idx = strings.Index(key, "/")
info.GRN.Kind = key[:idx]
info.GRN.UID = key[idx+1:]
}
}
case models.ObjectStoreScopeEntity:
{
idx = strings.Index(key, "/")
info.GRN.Kind = key[:idx]
info.GRN.UID = key[idx+1:]
}
default:
return info, fmt.Errorf("unsupported scope")
}
return info, nil
}

View File

@ -1,95 +0,0 @@
package router
import (
"context"
"fmt"
"testing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/store/kind"
"github.com/grafana/grafana/pkg/services/store/object"
"github.com/stretchr/testify/require"
)
func TestSimpleRouter(t *testing.T) {
ctx := context.Background()
router := &standardStoreRouter{
kinds: kind.NewKindRegistry(),
}
info, err := router.Route(ctx, &object.GRN{
UID: "path/to/file",
})
require.Error(t, err) // needs OrgID
type routeScenario struct {
GRN *object.GRN
Error string
Key string
}
scenarios := []routeScenario{{
Error: "missing TenantId",
GRN: &object.GRN{Scope: "x"},
}, {
Error: "unknown Kind: xyz",
GRN: &object.GRN{
TenantId: 11,
Scope: models.ObjectStoreScopeDrive,
UID: "path/to/file",
Kind: "xyz",
},
}, {
Key: "11/drive/path/to/file-dashboard.json",
GRN: &object.GRN{
TenantId: 11,
Scope: models.ObjectStoreScopeDrive,
UID: "path/to/file",
Kind: "dashboard",
},
}, {
Key: "11/drive/path/to/folder/__folder.json",
GRN: &object.GRN{
TenantId: 11,
Scope: models.ObjectStoreScopeDrive,
UID: "path/to/folder",
Kind: "folder",
},
}, {
Key: "10/drive/path/to/file.png",
GRN: &object.GRN{
TenantId: 10,
Scope: models.ObjectStoreScopeDrive,
UID: "path/to/file",
Kind: "png",
},
}, {
Key: "10/entity/playlist/aaaaa", // ?.json better or not?
GRN: &object.GRN{
TenantId: 10,
Scope: models.ObjectStoreScopeEntity,
UID: "aaaaa",
Kind: "playlist",
},
}}
for idx, check := range scenarios {
testID := fmt.Sprintf("[%d] %s", idx, check.Key)
// Read the key from the GRN
info, err = router.Route(ctx, check.GRN)
if check.Error == "" {
require.NoError(t, err, testID)
} else {
require.Error(t, err, testID)
require.Equal(t, check.Error, err.Error(), testID)
continue
}
// Check that the key matched
require.Equal(t, check.Key, info.Key, testID)
// Now try to parse the same key again
out, err := router.RouteFromKey(ctx, info.Key)
require.NoError(t, err, testID)
require.Equal(t, check.GRN, out.GRN, testID)
}
}