Repository: apigee/registry
Pull request #415 (2022-01-05 20:42:49)

Add a new admin API method to return database information.
This adds a GetStorage RPC to the admin service that returns a list of tables in the backend storage and the number of entries stored in each table. It is separate from GetStatus because it adds a small overhead (some database queries) and might be worth extending in the future to include other information such as schemas, index descriptions, and bytes of storage required for each entity type.

Addresses #412


Patch:
diff --git a/cmd/apg/admin_service.go b/cmd/apg/admin_service.go
index 9d5be72..c29e0eb 100644
--- a/cmd/apg/admin_service.go
+++ b/cmd/apg/admin_service.go
@@ -18,6 +18,7 @@ var AdminConfig *viper.Viper
 var AdminClient *gapic.AdminClient
 var AdminSubCommands []string = []string{
 	"get-status",
+	"get-storage",
 	"migrate-database",
 	"poll-migrate-database", "list-projects",
 	"get-project",
diff --git a/cmd/apg/get-storage.go b/cmd/apg/get-storage.go
new file mode 100644
index 0000000..b7b16f9
--- /dev/null
+++ b/cmd/apg/get-storage.go
@@ -0,0 +1,44 @@
+// Code generated. DO NOT EDIT.
+
+package main
+
+import (
+	"github.com/spf13/cobra"
+
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	"fmt"
+)
+
+var GetStorageInput emptypb.Empty
+
+func init() {
+	AdminServiceCmd.AddCommand(GetStorageCmd)
+
+}
+
+var GetStorageCmd = &cobra.Command{
+	Use:   "get-storage",
+	Short: "GetStorage returns information about the storage...",
+	Long:  "GetStorage returns information about the storage used by the service.  (-- api-linter: core::0131::request-message-name=disabled     ...",
+	PreRun: func(cmd *cobra.Command, args []string) {
+
+	},
+	RunE: func(cmd *cobra.Command, args []string) (err error) {
+
+		if Verbose {
+			printVerboseInput("Admin", "GetStorage", &GetStorageInput)
+		}
+		resp, err := AdminClient.GetStorage(ctx, &GetStorageInput)
+		if err != nil {
+			return err
+		}
+
+		if Verbose {
+			fmt.Print("Output: ")
+		}
+		printMessage(resp)
+
+		return err
+	},
+}
diff --git a/gapic/admin_client.go b/gapic/admin_client.go
index 7132ab0..fb821f4 100644
--- a/gapic/admin_client.go
+++ b/gapic/admin_client.go
@@ -1,4 +1,4 @@
-// Copyright 2021 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@ var newAdminClientHook clientHook
 // AdminCallOptions contains the retry settings for each method of AdminClient.
 type AdminCallOptions struct {
 	GetStatus       []gax.CallOption
+	GetStorage      []gax.CallOption
 	MigrateDatabase []gax.CallOption
 	ListProjects    []gax.CallOption
 	GetProject      []gax.CallOption
@@ -66,6 +67,7 @@ func defaultAdminGRPCClientOptions() []option.ClientOption {
 func defaultAdminCallOptions() *AdminCallOptions {
 	return &AdminCallOptions{
 		GetStatus:       []gax.CallOption{},
+		GetStorage:      []gax.CallOption{},
 		MigrateDatabase: []gax.CallOption{},
 		ListProjects:    []gax.CallOption{},
 		GetProject:      []gax.CallOption{},
@@ -81,6 +83,7 @@ type internalAdminClient interface {
 	setGoogleClientInfo(...string)
 	Connection() *grpc.ClientConn
 	GetStatus(context.Context, *emptypb.Empty, ...gax.CallOption) (*rpcpb.Status, error)
+	GetStorage(context.Context, *emptypb.Empty, ...gax.CallOption) (*rpcpb.Storage, error)
 	MigrateDatabase(context.Context, *rpcpb.MigrateDatabaseRequest, ...gax.CallOption) (*MigrateDatabaseOperation, error)
 	MigrateDatabaseOperation(name string) *MigrateDatabaseOperation
 	ListProjects(context.Context, *rpcpb.ListProjectsRequest, ...gax.CallOption) *ProjectIterator
@@ -141,6 +144,17 @@ func (c *AdminClient) GetStatus(ctx context.Context, req *emptypb.Empty, opts ..
 	return c.internalClient.GetStatus(ctx, req, opts...)
 }
 
+// GetStorage getStorage returns information about the storage used by the service.
+// (– api-linter: core::0131::request-message-name=disabled
+// aip.dev/not-precedent (at http://aip.dev/not-precedent): Not in the official API. –)
+// (– api-linter: core::0131::method-signature=disabled
+// aip.dev/not-precedent (at http://aip.dev/not-precedent): Not in the official API. –)
+// (– api-linter: core::0131::http-uri-name=disabled
+// aip.dev/not-precedent (at http://aip.dev/not-precedent): Not in the official API. –)
+func (c *AdminClient) GetStorage(ctx context.Context, req *emptypb.Empty, opts ...gax.CallOption) (*rpcpb.Storage, error) {
+	return c.internalClient.GetStorage(ctx, req, opts...)
+}
+
 // MigrateDatabase migrateDatabase attempts to migrate the database to the current schema.
 func (c *AdminClient) MigrateDatabase(ctx context.Context, req *rpcpb.MigrateDatabaseRequest, opts ...gax.CallOption) (*MigrateDatabaseOperation, error) {
 	return c.internalClient.MigrateDatabase(ctx, req, opts...)
@@ -298,6 +312,21 @@ func (c *adminGRPCClient) GetStatus(ctx context.Context, req *emptypb.Empty, opt
 	return resp, nil
 }
 
+func (c *adminGRPCClient) GetStorage(ctx context.Context, req *emptypb.Empty, opts ...gax.CallOption) (*rpcpb.Storage, error) {
+	ctx = insertMetadata(ctx, c.xGoogMetadata)
+	opts = append((*c.CallOptions).GetStorage[0:len((*c.CallOptions).GetStorage):len((*c.CallOptions).GetStorage)], opts...)
+	var resp *rpcpb.Storage
+	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
+		var err error
+		resp, err = c.adminClient.GetStorage(ctx, req, settings.GRPC...)
+		return err
+	}, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return resp, nil
+}
+
 func (c *adminGRPCClient) MigrateDatabase(ctx context.Context, req *rpcpb.MigrateDatabaseRequest, opts ...gax.CallOption) (*MigrateDatabaseOperation, error) {
 	ctx = insertMetadata(ctx, c.xGoogMetadata)
 	opts = append((*c.CallOptions).MigrateDatabase[0:len((*c.CallOptions).MigrateDatabase):len((*c.CallOptions).MigrateDatabase)], opts...)
diff --git a/gapic/doc.go b/gapic/doc.go
index 024477b..304499f 100644
--- a/gapic/doc.go
+++ b/gapic/doc.go
@@ -1,4 +1,4 @@
-// Copyright 2021 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/gapic/registry_client.go b/gapic/registry_client.go
index 1ea31ef..aff6ee7 100644
--- a/gapic/registry_client.go
+++ b/gapic/registry_client.go
@@ -1,4 +1,4 @@
-// Copyright 2021 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/google/cloud/apigeeregistry/v1/admin_models.proto b/google/cloud/apigeeregistry/v1/admin_models.proto
index 1322bf5..efa2699 100644
--- a/google/cloud/apigeeregistry/v1/admin_models.proto
+++ b/google/cloud/apigeeregistry/v1/admin_models.proto
@@ -25,6 +25,31 @@ option java_multiple_files = true;
 option java_outer_classname = "AdminModelsProto";
 option go_package = "github.com/apigee/registry/rpc;rpc";
 
+// Status represents the status of the service.
+message Status {
+  // A string describing the status.
+  string message = 1;
+}
+
+// Storage describes the data stored by the service.
+message Storage {
+  // A string describing the storage backend.
+  string description = 1;
+
+  // A description of a collection in the backend database.
+  message Collection {
+    // The name of the collection.
+    string name = 1;
+
+    // The number of entries in the collection. 
+    int64 count = 2;
+  }
+
+  // A list of collections in the storage backend.
+  // Collections are listed in alphabetical order.
+  repeated Collection collections = 2;
+}
+
 // A Project is a top-level description of a collection of APIs.
 // Typically there would be one project for an entire organization.
 // Note: in a Google Cloud deployment, this resource and associated methods
diff --git a/google/cloud/apigeeregistry/v1/admin_service.proto b/google/cloud/apigeeregistry/v1/admin_service.proto
index 3666772..d872153 100644
--- a/google/cloud/apigeeregistry/v1/admin_service.proto
+++ b/google/cloud/apigeeregistry/v1/admin_service.proto
@@ -48,6 +48,19 @@ service Admin {
     };
   }
 
+  // GetStorage returns information about the storage used by the service.
+  // (-- api-linter: core::0131::request-message-name=disabled
+  //     aip.dev/not-precedent: Not in the official API. --)
+  // (-- api-linter: core::0131::method-signature=disabled
+  //     aip.dev/not-precedent: Not in the official API. --)
+  // (-- api-linter: core::0131::http-uri-name=disabled
+  //     aip.dev/not-precedent: Not in the official API. --)
+  rpc GetStorage(protobuf.Empty) returns (Storage) {
+    option (google.api.http) = {
+      get: "/v1/storage"
+    };
+  }
+
   // MigrateDatabase attempts to migrate the database to the current schema.
   rpc MigrateDatabase(MigrateDatabaseRequest) returns (google.longrunning.Operation) {
     option (google.api.http) = {
@@ -110,13 +123,6 @@ service Admin {
   }
 }
 
-// Response message for GetStatus.
-// GetStatus is not included in hosted versions of the API.
-message Status {
-  // A string describing the status.
-  string message = 1;
-}
-
 // Request message for MigrateDatabase.
 message MigrateDatabaseRequest {
   // A string describing the kind of migration to perform.
diff --git a/rpc/admin_models.pb.go b/rpc/admin_models.pb.go
index 3b80d2a..0ad9787 100644
--- a/rpc/admin_models.pb.go
+++ b/rpc/admin_models.pb.go
@@ -37,6 +37,113 @@ const (
 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
+// Status represents the status of the service.
+type Status struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// A string describing the status.
+	Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+}
+
+func (x *Status) Reset() {
+	*x = Status{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Status) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status) ProtoMessage() {}
+
+func (x *Status) ProtoReflect() protoreflect.Message {
+	mi := &file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status.ProtoReflect.Descriptor instead.
+func (*Status) Descriptor() ([]byte, []int) {
+	return file_google_cloud_apigeeregistry_v1_admin_models_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Status) GetMessage() string {
+	if x != nil {
+		return x.Message
+	}
+	return ""
+}
+
+// Storage describes the data stored by the service.
+type Storage struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// A string describing the storage backend.
+	Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
+	// A list of collections in the storage backend.
+	Collections []*Storage_Collection `protobuf:"bytes,2,rep,name=collections,proto3" json:"collections,omitempty"`
+}
+
+func (x *Storage) Reset() {
+	*x = Storage{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Storage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Storage) ProtoMessage() {}
+
+func (x *Storage) ProtoReflect() protoreflect.Message {
+	mi := &file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Storage.ProtoReflect.Descriptor instead.
+func (*Storage) Descriptor() ([]byte, []int) {
+	return file_google_cloud_apigeeregistry_v1_admin_models_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Storage) GetDescription() string {
+	if x != nil {
+		return x.Description
+	}
+	return ""
+}
+
+func (x *Storage) GetCollections() []*Storage_Collection {
+	if x != nil {
+		return x.Collections
+	}
+	return nil
+}
+
 // A Project is a top-level description of a collection of APIs.
 // Typically there would be one project for an entire organization.
 // Note: in a Google Cloud deployment, this resource and associated methods
@@ -62,7 +169,7 @@ type Project struct {
 func (x *Project) Reset() {
 	*x = Project{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[0]
+		mi := &file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[2]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -75,7 +182,7 @@ func (x *Project) String() string {
 func (*Project) ProtoMessage() {}
 
 func (x *Project) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[0]
+	mi := &file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[2]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -88,7 +195,7 @@ func (x *Project) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Project.ProtoReflect.Descriptor instead.
 func (*Project) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_models_proto_rawDescGZIP(), []int{0}
+	return file_google_cloud_apigeeregistry_v1_admin_models_proto_rawDescGZIP(), []int{2}
 }
 
 func (x *Project) GetName() string {
@@ -126,6 +233,64 @@ func (x *Project) GetUpdateTime() *timestamppb.Timestamp {
 	return nil
 }
 
+// A description of a collection in the backend database.
+type Storage_Collection struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The name of the collection.
+	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	// The number of entries in the collection.
+	Count int64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
+}
+
+func (x *Storage_Collection) Reset() {
+	*x = Storage_Collection{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Storage_Collection) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Storage_Collection) ProtoMessage() {}
+
+func (x *Storage_Collection) ProtoReflect() protoreflect.Message {
+	mi := &file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Storage_Collection.ProtoReflect.Descriptor instead.
+func (*Storage_Collection) Descriptor() ([]byte, []int) {
+	return file_google_cloud_apigeeregistry_v1_admin_models_proto_rawDescGZIP(), []int{1, 0}
+}
+
+func (x *Storage_Collection) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *Storage_Collection) GetCount() int64 {
+	if x != nil {
+		return x.Count
+	}
+	return 0
+}
+
 var File_google_cloud_apigeeregistry_v1_admin_models_proto protoreflect.FileDescriptor
 
 var file_google_cloud_apigeeregistry_v1_admin_models_proto_rawDesc = []byte{
@@ -140,6 +305,20 @@ var file_google_cloud_apigeeregistry_v1_admin_models_proto_rawDesc = []byte{
 	0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
 	0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
 	0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x22, 0x22, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65,
+	0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73,
+	0x73, 0x61, 0x67, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
+	0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x12, 0x54, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67,
+	0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65,
+	0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x63, 0x6f, 0x6c,
+	0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x36, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, 0x6c,
+	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f,
+	0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74,
 	0x22, 0xa6, 0x02, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04,
 	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
 	0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
@@ -179,19 +358,23 @@ func file_google_cloud_apigeeregistry_v1_admin_models_proto_rawDescGZIP() []byte
 	return file_google_cloud_apigeeregistry_v1_admin_models_proto_rawDescData
 }
 
-var file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
 var file_google_cloud_apigeeregistry_v1_admin_models_proto_goTypes = []interface{}{
-	(*Project)(nil),               // 0: google.cloud.apigeeregistry.v1.Project
-	(*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp
+	(*Status)(nil),                // 0: google.cloud.apigeeregistry.v1.Status
+	(*Storage)(nil),               // 1: google.cloud.apigeeregistry.v1.Storage
+	(*Project)(nil),               // 2: google.cloud.apigeeregistry.v1.Project
+	(*Storage_Collection)(nil),    // 3: google.cloud.apigeeregistry.v1.Storage.Collection
+	(*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp
 }
 var file_google_cloud_apigeeregistry_v1_admin_models_proto_depIdxs = []int32{
-	1, // 0: google.cloud.apigeeregistry.v1.Project.create_time:type_name -> google.protobuf.Timestamp
-	1, // 1: google.cloud.apigeeregistry.v1.Project.update_time:type_name -> google.protobuf.Timestamp
-	2, // [2:2] is the sub-list for method output_type
-	2, // [2:2] is the sub-list for method input_type
-	2, // [2:2] is the sub-list for extension type_name
-	2, // [2:2] is the sub-list for extension extendee
-	0, // [0:2] is the sub-list for field type_name
+	3, // 0: google.cloud.apigeeregistry.v1.Storage.collections:type_name -> google.cloud.apigeeregistry.v1.Storage.Collection
+	4, // 1: google.cloud.apigeeregistry.v1.Project.create_time:type_name -> google.protobuf.Timestamp
+	4, // 2: google.cloud.apigeeregistry.v1.Project.update_time:type_name -> google.protobuf.Timestamp
+	3, // [3:3] is the sub-list for method output_type
+	3, // [3:3] is the sub-list for method input_type
+	3, // [3:3] is the sub-list for extension type_name
+	3, // [3:3] is the sub-list for extension extendee
+	0, // [0:3] is the sub-list for field type_name
 }
 
 func init() { file_google_cloud_apigeeregistry_v1_admin_models_proto_init() }
@@ -201,6 +384,30 @@ func file_google_cloud_apigeeregistry_v1_admin_models_proto_init() {
 	}
 	if !protoimpl.UnsafeEnabled {
 		file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Status); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Storage); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*Project); i {
 			case 0:
 				return &v.state
@@ -212,6 +419,18 @@ func file_google_cloud_apigeeregistry_v1_admin_models_proto_init() {
 				return nil
 			}
 		}
+		file_google_cloud_apigeeregistry_v1_admin_models_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Storage_Collection); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
@@ -219,7 +438,7 @@ func file_google_cloud_apigeeregistry_v1_admin_models_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_google_cloud_apigeeregistry_v1_admin_models_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   1,
+			NumMessages:   4,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/rpc/admin_service.pb.go b/rpc/admin_service.pb.go
index 38d50a5..2e9e6e7 100644
--- a/rpc/admin_service.pb.go
+++ b/rpc/admin_service.pb.go
@@ -39,56 +39,6 @@ const (
 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
-// Response message for GetStatus.
-// GetStatus is not included in hosted versions of the API.
-type Status struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	// A string describing the status.
-	Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
-}
-
-func (x *Status) Reset() {
-	*x = Status{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Status) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Status) ProtoMessage() {}
-
-func (x *Status) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[0]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Status.ProtoReflect.Descriptor instead.
-func (*Status) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *Status) GetMessage() string {
-	if x != nil {
-		return x.Message
-	}
-	return ""
-}
-
 // Request message for MigrateDatabase.
 type MigrateDatabaseRequest struct {
 	state         protoimpl.MessageState
@@ -103,7 +53,7 @@ type MigrateDatabaseRequest struct {
 func (x *MigrateDatabaseRequest) Reset() {
 	*x = MigrateDatabaseRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[1]
+		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[0]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -116,7 +66,7 @@ func (x *MigrateDatabaseRequest) String() string {
 func (*MigrateDatabaseRequest) ProtoMessage() {}
 
 func (x *MigrateDatabaseRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[1]
+	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[0]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -129,7 +79,7 @@ func (x *MigrateDatabaseRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use MigrateDatabaseRequest.ProtoReflect.Descriptor instead.
 func (*MigrateDatabaseRequest) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{1}
+	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{0}
 }
 
 func (x *MigrateDatabaseRequest) GetKind() string {
@@ -149,7 +99,7 @@ type MigrateDatabaseMetadata struct {
 func (x *MigrateDatabaseMetadata) Reset() {
 	*x = MigrateDatabaseMetadata{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[2]
+		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[1]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -162,7 +112,7 @@ func (x *MigrateDatabaseMetadata) String() string {
 func (*MigrateDatabaseMetadata) ProtoMessage() {}
 
 func (x *MigrateDatabaseMetadata) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[2]
+	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[1]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -175,7 +125,7 @@ func (x *MigrateDatabaseMetadata) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use MigrateDatabaseMetadata.ProtoReflect.Descriptor instead.
 func (*MigrateDatabaseMetadata) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{2}
+	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{1}
 }
 
 // Response message for MigrateDatabase.
@@ -191,7 +141,7 @@ type MigrateDatabaseResponse struct {
 func (x *MigrateDatabaseResponse) Reset() {
 	*x = MigrateDatabaseResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[3]
+		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[2]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -204,7 +154,7 @@ func (x *MigrateDatabaseResponse) String() string {
 func (*MigrateDatabaseResponse) ProtoMessage() {}
 
 func (x *MigrateDatabaseResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[3]
+	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[2]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -217,7 +167,7 @@ func (x *MigrateDatabaseResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use MigrateDatabaseResponse.ProtoReflect.Descriptor instead.
 func (*MigrateDatabaseResponse) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{3}
+	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{2}
 }
 
 func (x *MigrateDatabaseResponse) GetMessage() string {
@@ -254,7 +204,7 @@ type ListProjectsRequest struct {
 func (x *ListProjectsRequest) Reset() {
 	*x = ListProjectsRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[4]
+		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -267,7 +217,7 @@ func (x *ListProjectsRequest) String() string {
 func (*ListProjectsRequest) ProtoMessage() {}
 
 func (x *ListProjectsRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[4]
+	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -280,7 +230,7 @@ func (x *ListProjectsRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListProjectsRequest.ProtoReflect.Descriptor instead.
 func (*ListProjectsRequest) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{4}
+	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *ListProjectsRequest) GetPageSize() int32 {
@@ -320,7 +270,7 @@ type ListProjectsResponse struct {
 func (x *ListProjectsResponse) Reset() {
 	*x = ListProjectsResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[5]
+		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -333,7 +283,7 @@ func (x *ListProjectsResponse) String() string {
 func (*ListProjectsResponse) ProtoMessage() {}
 
 func (x *ListProjectsResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[5]
+	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -346,7 +296,7 @@ func (x *ListProjectsResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListProjectsResponse.ProtoReflect.Descriptor instead.
 func (*ListProjectsResponse) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{5}
+	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *ListProjectsResponse) GetProjects() []*Project {
@@ -377,7 +327,7 @@ type GetProjectRequest struct {
 func (x *GetProjectRequest) Reset() {
 	*x = GetProjectRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[6]
+		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[5]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -390,7 +340,7 @@ func (x *GetProjectRequest) String() string {
 func (*GetProjectRequest) ProtoMessage() {}
 
 func (x *GetProjectRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[6]
+	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[5]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -403,7 +353,7 @@ func (x *GetProjectRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetProjectRequest.ProtoReflect.Descriptor instead.
 func (*GetProjectRequest) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{6}
+	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{5}
 }
 
 func (x *GetProjectRequest) GetName() string {
@@ -434,7 +384,7 @@ type CreateProjectRequest struct {
 func (x *CreateProjectRequest) Reset() {
 	*x = CreateProjectRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[7]
+		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[6]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -447,7 +397,7 @@ func (x *CreateProjectRequest) String() string {
 func (*CreateProjectRequest) ProtoMessage() {}
 
 func (x *CreateProjectRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[7]
+	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[6]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -460,7 +410,7 @@ func (x *CreateProjectRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CreateProjectRequest.ProtoReflect.Descriptor instead.
 func (*CreateProjectRequest) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{7}
+	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{6}
 }
 
 func (x *CreateProjectRequest) GetProject() *Project {
@@ -501,7 +451,7 @@ type UpdateProjectRequest struct {
 func (x *UpdateProjectRequest) Reset() {
 	*x = UpdateProjectRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[8]
+		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[7]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -514,7 +464,7 @@ func (x *UpdateProjectRequest) String() string {
 func (*UpdateProjectRequest) ProtoMessage() {}
 
 func (x *UpdateProjectRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[8]
+	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[7]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -527,7 +477,7 @@ func (x *UpdateProjectRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use UpdateProjectRequest.ProtoReflect.Descriptor instead.
 func (*UpdateProjectRequest) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{8}
+	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{7}
 }
 
 func (x *UpdateProjectRequest) GetProject() *Project {
@@ -565,7 +515,7 @@ type DeleteProjectRequest struct {
 func (x *DeleteProjectRequest) Reset() {
 	*x = DeleteProjectRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[9]
+		mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[8]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -578,7 +528,7 @@ func (x *DeleteProjectRequest) String() string {
 func (*DeleteProjectRequest) ProtoMessage() {}
 
 func (x *DeleteProjectRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[9]
+	mi := &file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[8]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -591,7 +541,7 @@ func (x *DeleteProjectRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DeleteProjectRequest.ProtoReflect.Descriptor instead.
 func (*DeleteProjectRequest) Descriptor() ([]byte, []int) {
-	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{9}
+	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP(), []int{8}
 }
 
 func (x *DeleteProjectRequest) GetName() string {
@@ -625,139 +575,143 @@ var file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDesc = []byte{
 	0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
 	0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67, 0x6f,
 	0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x66, 0x69,
-	0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x22,
-	0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
-	0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
-	0x67, 0x65, 0x22, 0x2c, 0x0a, 0x16, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74,
-	0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04,
-	0x6b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64,
-	0x22, 0x19, 0x0a, 0x17, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62,
-	0x61, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x33, 0x0a, 0x17, 0x4d,
-	0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
-	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
-	0x22, 0x69, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f,
-	0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65,
-	0x53, 0x69, 0x7a, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b,
-	0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f,
-	0x6b, 0x65, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x83, 0x01, 0x0a, 0x14,
-	0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73,
-	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
-	0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69,
-	0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52,
-	0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 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, 0x22, 0x56, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x42, 0x2d, 0xe0, 0x41, 0x02, 0xfa, 0x41, 0x27, 0x0a, 0x25, 0x61, 0x70,
-	0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x67, 0x6f, 0x6f,
-	0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x72, 0x6f, 0x6a,
-	0x65, 0x63, 0x74, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x7d, 0x0a, 0x14, 0x43, 0x72, 0x65,
-	0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x12, 0x46, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01,
+	0x65, 0x6c, 0x64, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2c,
+	0x0a, 0x16, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73,
+	0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x19, 0x0a, 0x17,
+	0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x4d,
+	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x33, 0x0a, 0x17, 0x4d, 0x69, 0x67, 0x72, 0x61,
+	0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x69, 0x0a, 0x13,
+	0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, 0x7a, 0x65,
+	0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12,
+	0x16, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x83, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74,
+	0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x12, 0x43, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03,
 	0x28, 0x0b, 0x32, 0x27, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75,
 	0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
-	0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x03, 0xe0, 0x41, 0x02,
-	0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f,
-	0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70,
-	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x64,
-	0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x12, 0x46, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x27, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75,
-	0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
-	0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x03, 0xe0, 0x41, 0x02,
-	0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64,
-	0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
-	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
-	0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61,
-	0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f,
-	0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61,
-	0x6c, 0x6c, 0x6f, 0x77, 0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x22, 0x59, 0x0a, 0x14, 0x44,
-	0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x42, 0x2d, 0xe0, 0x41, 0x02, 0xfa, 0x41, 0x27, 0x0a, 0x25, 0x61, 0x70, 0x69, 0x67, 0x65,
-	0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
-	0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
-	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x32, 0xc4, 0x08, 0x0a, 0x05, 0x41, 0x64, 0x6d, 0x69, 0x6e,
-	0x12, 0x5f, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e,
-	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
-	0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x26, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63,
-	0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73,
-	0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x12, 0x82,
-	0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75,
-	0x73, 0x12, 0xb2, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74,
+	0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x08, 0x70, 0x72, 0x6f,
+	0x6a, 0x65, 0x63, 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, 0x22, 0x56, 0x0a,
+	0x11, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x41, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x42, 0x2d, 0xe0, 0x41, 0x02, 0xfa, 0x41, 0x27, 0x0a, 0x25, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65,
+	0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61,
+	0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52,
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x7d, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50,
+	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a,
+	0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27,
+	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70,
+	0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e,
+	0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x07, 0x70, 0x72,
+	0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
+	0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65,
+	0x63, 0x74, 0x49, 0x64, 0x22, 0xc0, 0x01, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50,
+	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a,
+	0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27,
+	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70,
+	0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e,
+	0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x03, 0xe0, 0x41, 0x02, 0x52, 0x07, 0x70, 0x72,
+	0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x3b, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f,
+	0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65,
+	0x6c, 0x64, 0x4d, 0x61, 0x73, 0x6b, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61,
+	0x73, 0x6b, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x6d, 0x69, 0x73, 0x73,
+	0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77,
+	0x4d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x22, 0x59, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74,
+	0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x41, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2d, 0xe0,
+	0x41, 0x02, 0xfa, 0x41, 0x27, 0x0a, 0x25, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67,
+	0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73,
+	0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x32, 0xb0, 0x09, 0x0a, 0x05, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x5f, 0x0a, 0x09,
+	0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
+	0x79, 0x1a, 0x26, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
+	0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e,
+	0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02,
+	0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x62, 0x0a,
+	0x0a, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f,
+	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
+	0x70, 0x74, 0x79, 0x1a, 0x27, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f,
+	0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
+	0x79, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x13, 0x82, 0xd3,
+	0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67,
+	0x65, 0x12, 0xba, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74,
 	0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x36, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63,
 	0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73,
 	0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61,
 	0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e,
 	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x6c, 0x6f, 0x6e, 0x67, 0x72, 0x75, 0x6e, 0x6e, 0x69,
-	0x6e, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x48, 0x82, 0xd3,
-	0xe4, 0x93, 0x02, 0x0d, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74,
-	0x65, 0xca, 0x41, 0x32, 0x0a, 0x17, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74,
-	0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x4d,
-	0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x4d, 0x65,
-	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x8f, 0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50,
-	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x33, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
-	0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67,
-	0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f,
-	0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x67,
-	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67,
-	0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69,
-	0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x12, 0x0c, 0x2f, 0x76, 0x31, 0x2f,
-	0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x8e, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74,
-	0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
-	0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67,
-	0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a,
-	0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x6f, 0x6f,
-	0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65,
-	0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a,
-	0x65, 0x63, 0x74, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31,
-	0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f,
-	0x2a, 0x7d, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0xa2, 0x01, 0x0a, 0x0d, 0x43, 0x72,
-	0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x34, 0x2e, 0x67, 0x6f,
-	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65,
-	0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65,
-	0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x1a, 0x27, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
-	0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e,
-	0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93,
-	0x02, 0x17, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73,
-	0x3a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0xda, 0x41, 0x12, 0x70, 0x72, 0x6f, 0x6a,
-	0x65, 0x63, 0x74, 0x2c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x12, 0xb4,
-	0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
-	0x12, 0x34, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e,
-	0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76,
-	0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
-	0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69,
-	0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22,
-	0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x32, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x70, 0x72,
-	0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x70, 0x72, 0x6f, 0x6a, 0x65,
-	0x63, 0x74, 0x73, 0x2f, 0x2a, 0x7d, 0x3a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0xda,
-	0x41, 0x13, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
-	0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x83, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
-	0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
-	0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67,
-	0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50,
-	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e,
-	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
-	0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x2a, 0x15, 0x2f,
-	0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
-	0x73, 0x2f, 0x2a, 0x7d, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x1a, 0x20, 0xca, 0x41, 0x1d,
-	0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x67,
-	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x42, 0x5d, 0x0a,
-	0x22, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75,
+	0x6e, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x50, 0x82, 0xd3,
+	0xe4, 0x93, 0x02, 0x15, 0x22, 0x13, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x69, 0x67, 0x72, 0x61, 0x74,
+	0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0xca, 0x41, 0x32, 0x0a, 0x17, 0x4d, 0x69,
+	0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x4d, 0x69, 0x67, 0x72, 0x61, 0x74, 0x65, 0x44, 0x61,
+	0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x8f,
+	0x01, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12,
+	0x33, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61,
+	0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31,
+	0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c,
+	0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
+	0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63,
+	0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93,
+	0x02, 0x0e, 0x12, 0x0c, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73,
+	0x12, 0x8e, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12,
+	0x31, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61,
+	0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31,
+	0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75,
 	0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
-	0x2e, 0x76, 0x31, 0x42, 0x11, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
-	0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
-	0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x2f, 0x72, 0x65, 0x67, 0x69,
-	0x73, 0x74, 0x72, 0x79, 0x2f, 0x72, 0x70, 0x63, 0x3b, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x33,
+	0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x24, 0x82, 0xd3, 0xe4,
+	0x93, 0x02, 0x17, 0x12, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x70,
+	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x2a, 0x7d, 0xda, 0x41, 0x04, 0x6e, 0x61, 0x6d,
+	0x65, 0x12, 0xa2, 0x01, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a,
+	0x65, 0x63, 0x74, 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f,
+	0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
+	0x79, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65,
+	0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72,
+	0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65,
+	0x63, 0x74, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x0c, 0x2f, 0x76, 0x31, 0x2f,
+	0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x3a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
+	0x74, 0xda, 0x41, 0x12, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2c, 0x70, 0x72, 0x6f, 0x6a,
+	0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x12, 0xb4, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74,
+	0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+	0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65,
+	0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+	0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27,
+	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70,
+	0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e,
+	0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x32,
+	0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x6e, 0x61,
+	0x6d, 0x65, 0x3d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x2a, 0x7d, 0x3a, 0x07,
+	0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0xda, 0x41, 0x13, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
+	0x74, 0x2c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x12, 0x83, 0x01,
+	0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12,
+	0x34, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61,
+	0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31,
+	0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x24, 0x82,
+	0xd3, 0xe4, 0x93, 0x02, 0x17, 0x2a, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x7b, 0x6e, 0x61, 0x6d, 0x65,
+	0x3d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x2a, 0x7d, 0xda, 0x41, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x1a, 0x20, 0xca, 0x41, 0x1d, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65, 0x72, 0x65,
+	0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69,
+	0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x42, 0x5d, 0x0a, 0x22, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x65, 0x65,
+	0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x42, 0x11, 0x41, 0x64, 0x6d,
+	0x69, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
+	0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x69,
+	0x67, 0x65, 0x65, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x72, 0x70, 0x63,
+	0x3b, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -772,44 +726,47 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescGZIP() []byt
 	return file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDescData
 }
 
-var file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
+var file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
 var file_google_cloud_apigeeregistry_v1_admin_service_proto_goTypes = []interface{}{
-	(*Status)(nil),                  // 0: google.cloud.apigeeregistry.v1.Status
-	(*MigrateDatabaseRequest)(nil),  // 1: google.cloud.apigeeregistry.v1.MigrateDatabaseRequest
-	(*MigrateDatabaseMetadata)(nil), // 2: google.cloud.apigeeregistry.v1.MigrateDatabaseMetadata
-	(*MigrateDatabaseResponse)(nil), // 3: google.cloud.apigeeregistry.v1.MigrateDatabaseResponse
-	(*ListProjectsRequest)(nil),     // 4: google.cloud.apigeeregistry.v1.ListProjectsRequest
-	(*ListProjectsResponse)(nil),    // 5: google.cloud.apigeeregistry.v1.ListProjectsResponse
-	(*GetProjectRequest)(nil),       // 6: google.cloud.apigeeregistry.v1.GetProjectRequest
-	(*CreateProjectRequest)(nil),    // 7: google.cloud.apigeeregistry.v1.CreateProjectRequest
-	(*UpdateProjectRequest)(nil),    // 8: google.cloud.apigeeregistry.v1.UpdateProjectRequest
-	(*DeleteProjectRequest)(nil),    // 9: google.cloud.apigeeregistry.v1.DeleteProjectRequest
-	(*Project)(nil),                 // 10: google.cloud.apigeeregistry.v1.Project
-	(*fieldmaskpb.FieldMask)(nil),   // 11: google.protobuf.FieldMask
-	(*emptypb.Empty)(nil),           // 12: google.protobuf.Empty
-	(*longrunning.Operation)(nil),   // 13: google.longrunning.Operation
+	(*MigrateDatabaseRequest)(nil),  // 0: google.cloud.apigeeregistry.v1.MigrateDatabaseRequest
+	(*MigrateDatabaseMetadata)(nil), // 1: google.cloud.apigeeregistry.v1.MigrateDatabaseMetadata
+	(*MigrateDatabaseResponse)(nil), // 2: google.cloud.apigeeregistry.v1.MigrateDatabaseResponse
+	(*ListProjectsRequest)(nil),     // 3: google.cloud.apigeeregistry.v1.ListProjectsRequest
+	(*ListProjectsResponse)(nil),    // 4: google.cloud.apigeeregistry.v1.ListProjectsResponse
+	(*GetProjectRequest)(nil),       // 5: google.cloud.apigeeregistry.v1.GetProjectRequest
+	(*CreateProjectRequest)(nil),    // 6: google.cloud.apigeeregistry.v1.CreateProjectRequest
+	(*UpdateProjectRequest)(nil),    // 7: google.cloud.apigeeregistry.v1.UpdateProjectRequest
+	(*DeleteProjectRequest)(nil),    // 8: google.cloud.apigeeregistry.v1.DeleteProjectRequest
+	(*Project)(nil),                 // 9: google.cloud.apigeeregistry.v1.Project
+	(*fieldmaskpb.FieldMask)(nil),   // 10: google.protobuf.FieldMask
+	(*emptypb.Empty)(nil),           // 11: google.protobuf.Empty
+	(*Status)(nil),                  // 12: google.cloud.apigeeregistry.v1.Status
+	(*Storage)(nil),                 // 13: google.cloud.apigeeregistry.v1.Storage
+	(*longrunning.Operation)(nil),   // 14: google.longrunning.Operation
 }
 var file_google_cloud_apigeeregistry_v1_admin_service_proto_depIdxs = []int32{
-	10, // 0: google.cloud.apigeeregistry.v1.ListProjectsResponse.projects:type_name -> google.cloud.apigeeregistry.v1.Project
-	10, // 1: google.cloud.apigeeregistry.v1.CreateProjectRequest.project:type_name -> google.cloud.apigeeregistry.v1.Project
-	10, // 2: google.cloud.apigeeregistry.v1.UpdateProjectRequest.project:type_name -> google.cloud.apigeeregistry.v1.Project
-	11, // 3: google.cloud.apigeeregistry.v1.UpdateProjectRequest.update_mask:type_name -> google.protobuf.FieldMask
-	12, // 4: google.cloud.apigeeregistry.v1.Admin.GetStatus:input_type -> google.protobuf.Empty
-	1,  // 5: google.cloud.apigeeregistry.v1.Admin.MigrateDatabase:input_type -> google.cloud.apigeeregistry.v1.MigrateDatabaseRequest
-	4,  // 6: google.cloud.apigeeregistry.v1.Admin.ListProjects:input_type -> google.cloud.apigeeregistry.v1.ListProjectsRequest
-	6,  // 7: google.cloud.apigeeregistry.v1.Admin.GetProject:input_type -> google.cloud.apigeeregistry.v1.GetProjectRequest
-	7,  // 8: google.cloud.apigeeregistry.v1.Admin.CreateProject:input_type -> google.cloud.apigeeregistry.v1.CreateProjectRequest
-	8,  // 9: google.cloud.apigeeregistry.v1.Admin.UpdateProject:input_type -> google.cloud.apigeeregistry.v1.UpdateProjectRequest
-	9,  // 10: google.cloud.apigeeregistry.v1.Admin.DeleteProject:input_type -> google.cloud.apigeeregistry.v1.DeleteProjectRequest
-	0,  // 11: google.cloud.apigeeregistry.v1.Admin.GetStatus:output_type -> google.cloud.apigeeregistry.v1.Status
-	13, // 12: google.cloud.apigeeregistry.v1.Admin.MigrateDatabase:output_type -> google.longrunning.Operation
-	5,  // 13: google.cloud.apigeeregistry.v1.Admin.ListProjects:output_type -> google.cloud.apigeeregistry.v1.ListProjectsResponse
-	10, // 14: google.cloud.apigeeregistry.v1.Admin.GetProject:output_type -> google.cloud.apigeeregistry.v1.Project
-	10, // 15: google.cloud.apigeeregistry.v1.Admin.CreateProject:output_type -> google.cloud.apigeeregistry.v1.Project
-	10, // 16: google.cloud.apigeeregistry.v1.Admin.UpdateProject:output_type -> google.cloud.apigeeregistry.v1.Project
-	12, // 17: google.cloud.apigeeregistry.v1.Admin.DeleteProject:output_type -> google.protobuf.Empty
-	11, // [11:18] is the sub-list for method output_type
-	4,  // [4:11] is the sub-list for method input_type
+	9,  // 0: google.cloud.apigeeregistry.v1.ListProjectsResponse.projects:type_name -> google.cloud.apigeeregistry.v1.Project
+	9,  // 1: google.cloud.apigeeregistry.v1.CreateProjectRequest.project:type_name -> google.cloud.apigeeregistry.v1.Project
+	9,  // 2: google.cloud.apigeeregistry.v1.UpdateProjectRequest.project:type_name -> google.cloud.apigeeregistry.v1.Project
+	10, // 3: google.cloud.apigeeregistry.v1.UpdateProjectRequest.update_mask:type_name -> google.protobuf.FieldMask
+	11, // 4: google.cloud.apigeeregistry.v1.Admin.GetStatus:input_type -> google.protobuf.Empty
+	11, // 5: google.cloud.apigeeregistry.v1.Admin.GetStorage:input_type -> google.protobuf.Empty
+	0,  // 6: google.cloud.apigeeregistry.v1.Admin.MigrateDatabase:input_type -> google.cloud.apigeeregistry.v1.MigrateDatabaseRequest
+	3,  // 7: google.cloud.apigeeregistry.v1.Admin.ListProjects:input_type -> google.cloud.apigeeregistry.v1.ListProjectsRequest
+	5,  // 8: google.cloud.apigeeregistry.v1.Admin.GetProject:input_type -> google.cloud.apigeeregistry.v1.GetProjectRequest
+	6,  // 9: google.cloud.apigeeregistry.v1.Admin.CreateProject:input_type -> google.cloud.apigeeregistry.v1.CreateProjectRequest
+	7,  // 10: google.cloud.apigeeregistry.v1.Admin.UpdateProject:input_type -> google.cloud.apigeeregistry.v1.UpdateProjectRequest
+	8,  // 11: google.cloud.apigeeregistry.v1.Admin.DeleteProject:input_type -> google.cloud.apigeeregistry.v1.DeleteProjectRequest
+	12, // 12: google.cloud.apigeeregistry.v1.Admin.GetStatus:output_type -> google.cloud.apigeeregistry.v1.Status
+	13, // 13: google.cloud.apigeeregistry.v1.Admin.GetStorage:output_type -> google.cloud.apigeeregistry.v1.Storage
+	14, // 14: google.cloud.apigeeregistry.v1.Admin.MigrateDatabase:output_type -> google.longrunning.Operation
+	4,  // 15: google.cloud.apigeeregistry.v1.Admin.ListProjects:output_type -> google.cloud.apigeeregistry.v1.ListProjectsResponse
+	9,  // 16: google.cloud.apigeeregistry.v1.Admin.GetProject:output_type -> google.cloud.apigeeregistry.v1.Project
+	9,  // 17: google.cloud.apigeeregistry.v1.Admin.CreateProject:output_type -> google.cloud.apigeeregistry.v1.Project
+	9,  // 18: google.cloud.apigeeregistry.v1.Admin.UpdateProject:output_type -> google.cloud.apigeeregistry.v1.Project
+	11, // 19: google.cloud.apigeeregistry.v1.Admin.DeleteProject:output_type -> google.protobuf.Empty
+	12, // [12:20] is the sub-list for method output_type
+	4,  // [4:12] is the sub-list for method input_type
 	4,  // [4:4] is the sub-list for extension type_name
 	4,  // [4:4] is the sub-list for extension extendee
 	0,  // [0:4] is the sub-list for field type_name
@@ -823,18 +780,6 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_init() {
 	file_google_cloud_apigeeregistry_v1_admin_models_proto_init()
 	if !protoimpl.UnsafeEnabled {
 		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Status); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*MigrateDatabaseRequest); i {
 			case 0:
 				return &v.state
@@ -846,7 +791,7 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_init() {
 				return nil
 			}
 		}
-		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*MigrateDatabaseMetadata); i {
 			case 0:
 				return &v.state
@@ -858,7 +803,7 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_init() {
 				return nil
 			}
 		}
-		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*MigrateDatabaseResponse); i {
 			case 0:
 				return &v.state
@@ -870,7 +815,7 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_init() {
 				return nil
 			}
 		}
-		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*ListProjectsRequest); i {
 			case 0:
 				return &v.state
@@ -882,7 +827,7 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_init() {
 				return nil
 			}
 		}
-		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*ListProjectsResponse); i {
 			case 0:
 				return &v.state
@@ -894,7 +839,7 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_init() {
 				return nil
 			}
 		}
-		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*GetProjectRequest); i {
 			case 0:
 				return &v.state
@@ -906,7 +851,7 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_init() {
 				return nil
 			}
 		}
-		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*CreateProjectRequest); i {
 			case 0:
 				return &v.state
@@ -918,7 +863,7 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_init() {
 				return nil
 			}
 		}
-		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*UpdateProjectRequest); i {
 			case 0:
 				return &v.state
@@ -930,7 +875,7 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_init() {
 				return nil
 			}
 		}
-		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+		file_google_cloud_apigeeregistry_v1_admin_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*DeleteProjectRequest); i {
 			case 0:
 				return &v.state
@@ -949,7 +894,7 @@ func file_google_cloud_apigeeregistry_v1_admin_service_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_google_cloud_apigeeregistry_v1_admin_service_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   10,
+			NumMessages:   9,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/rpc/admin_service_grpc.pb.go b/rpc/admin_service_grpc.pb.go
index 87ba51b..bcd37dd 100644
--- a/rpc/admin_service_grpc.pb.go
+++ b/rpc/admin_service_grpc.pb.go
@@ -1,4 +1,8 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.2.0
+// - protoc             v3.18.1
+// source: google/cloud/apigeeregistry/v1/admin_service.proto
 
 package rpc
 
@@ -29,6 +33,14 @@ type AdminClient interface {
 	// (-- api-linter: core::0131::http-uri-name=disabled
 	//     aip.dev/not-precedent: Not in the official API. --)
 	GetStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Status, error)
+	// GetStorage returns information about the storage used by the service.
+	// (-- api-linter: core::0131::request-message-name=disabled
+	//     aip.dev/not-precedent: Not in the official API. --)
+	// (-- api-linter: core::0131::method-signature=disabled
+	//     aip.dev/not-precedent: Not in the official API. --)
+	// (-- api-linter: core::0131::http-uri-name=disabled
+	//     aip.dev/not-precedent: Not in the official API. --)
+	GetStorage(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Storage, error)
 	// MigrateDatabase attempts to migrate the database to the current schema.
 	MigrateDatabase(ctx context.Context, in *MigrateDatabaseRequest, opts ...grpc.CallOption) (*longrunning.Operation, error)
 	// ListProjects returns matching projects.
@@ -69,6 +81,15 @@ func (c *adminClient) GetStatus(ctx context.Context, in *emptypb.Empty, opts ...
 	return out, nil
 }
 
+func (c *adminClient) GetStorage(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Storage, error) {
+	out := new(Storage)
+	err := c.cc.Invoke(ctx, "/google.cloud.apigeeregistry.v1.Admin/GetStorage", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 func (c *adminClient) MigrateDatabase(ctx context.Context, in *MigrateDatabaseRequest, opts ...grpc.CallOption) (*longrunning.Operation, error) {
 	out := new(longrunning.Operation)
 	err := c.cc.Invoke(ctx, "/google.cloud.apigeeregistry.v1.Admin/MigrateDatabase", in, out, opts...)
@@ -135,6 +156,14 @@ type AdminServer interface {
 	// (-- api-linter: core::0131::http-uri-name=disabled
 	//     aip.dev/not-precedent: Not in the official API. --)
 	GetStatus(context.Context, *emptypb.Empty) (*Status, error)
+	// GetStorage returns information about the storage used by the service.
+	// (-- api-linter: core::0131::request-message-name=disabled
+	//     aip.dev/not-precedent: Not in the official API. --)
+	// (-- api-linter: core::0131::method-signature=disabled
+	//     aip.dev/not-precedent: Not in the official API. --)
+	// (-- api-linter: core::0131::http-uri-name=disabled
+	//     aip.dev/not-precedent: Not in the official API. --)
+	GetStorage(context.Context, *emptypb.Empty) (*Storage, error)
 	// MigrateDatabase attempts to migrate the database to the current schema.
 	MigrateDatabase(context.Context, *MigrateDatabaseRequest) (*longrunning.Operation, error)
 	// ListProjects returns matching projects.
@@ -166,6 +195,9 @@ type UnimplementedAdminServer struct {
 func (UnimplementedAdminServer) GetStatus(context.Context, *emptypb.Empty) (*Status, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method GetStatus not implemented")
 }
+func (UnimplementedAdminServer) GetStorage(context.Context, *emptypb.Empty) (*Storage, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetStorage not implemented")
+}
 func (UnimplementedAdminServer) MigrateDatabase(context.Context, *MigrateDatabaseRequest) (*longrunning.Operation, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method MigrateDatabase not implemented")
 }
@@ -215,6 +247,24 @@ func _Admin_GetStatus_Handler(srv interface{}, ctx context.Context, dec func(int
 	return interceptor(ctx, in, info, handler)
 }
 
+func _Admin_GetStorage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(emptypb.Empty)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AdminServer).GetStorage(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/google.cloud.apigeeregistry.v1.Admin/GetStorage",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AdminServer).GetStorage(ctx, req.(*emptypb.Empty))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 func _Admin_MigrateDatabase_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(MigrateDatabaseRequest)
 	if err := dec(in); err != nil {
@@ -334,6 +384,10 @@ var Admin_ServiceDesc = grpc.ServiceDesc{
 			MethodName: "GetStatus",
 			Handler:    _Admin_GetStatus_Handler,
 		},
+		{
+			MethodName: "GetStorage",
+			Handler:    _Admin_GetStorage_Handler,
+		},
 		{
 			MethodName: "MigrateDatabase",
 			Handler:    _Admin_MigrateDatabase_Handler,
diff --git a/rpc/registry_service_grpc.pb.go b/rpc/registry_service_grpc.pb.go
index 48f3169..160e984 100644
--- a/rpc/registry_service_grpc.pb.go
+++ b/rpc/registry_service_grpc.pb.go
@@ -1,4 +1,8 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.2.0
+// - protoc             v3.18.1
+// source: google/cloud/apigeeregistry/v1/registry_service.proto
 
 package rpc
 
diff --git a/server/registry/actions_storage.go b/server/registry/actions_storage.go
new file mode 100644
index 0000000..a86413c
--- /dev/null
+++ b/server/registry/actions_storage.go
@@ -0,0 +1,54 @@
+// Copyright 2022 Google LLC. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package registry
+
+import (
+	"context"
+
+	"github.com/apigee/registry/rpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+	"google.golang.org/protobuf/types/known/emptypb"
+)
+
+// GetStorage handles the corresponding API request.
+func (s *RegistryServer) GetStorage(ctx context.Context, req *emptypb.Empty) (*rpc.Storage, error) {
+	db, err := s.getStorageClient(ctx)
+	if err != nil {
+		return nil, status.Error(codes.Unavailable, err.Error())
+	}
+	defer db.Close()
+	tableNames, err := db.TableNames()
+	if err != nil {
+		return nil, err
+	}
+	collections := make([]*rpc.Storage_Collection, 0)
+	for _, tableName := range tableNames {
+		count, err := db.RowCount(tableName)
+		if err != nil {
+			return nil, err
+		}
+		collections = append(collections,
+			&rpc.Storage_Collection{
+				Name:  tableName,
+				Count: count,
+			},
+		)
+	}
+	return &rpc.Storage{
+		Description: db.DatabaseName(),
+		Collections: collections,
+	}, nil
+}
diff --git a/server/registry/internal/storage/gorm/client.go b/server/registry/internal/storage/gorm/client.go
index 2efb359..c2995f8 100644
--- a/server/registry/internal/storage/gorm/client.go
+++ b/server/registry/internal/storage/gorm/client.go
@@ -21,6 +21,8 @@ import (
 
 	_ "github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/dialers/postgres"
 	"github.com/apigee/registry/server/registry/internal/storage/models"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
 	"gorm.io/driver/postgres"
 	"gorm.io/driver/sqlite"
 	"gorm.io/gorm"
@@ -349,3 +351,30 @@ func (c *Client) GetRecentDeploymentRevisions(ctx context.Context, offset int32,
 func (c *Client) Migrate(kind string) error {
 	return c.db.AutoMigrate(entities...)
 }
+
+func (c *Client) DatabaseName() string {
+	return c.db.Name()
+}
+
+func (c *Client) TableNames() ([]string, error) {
+	var tableNames []string
+	switch c.db.Name() {
+	case "postgres":
+		if err := c.db.Table("information_schema.tables").Where("table_schema = ?", "public").Order("table_name").Pluck("table_name", &tableNames).Error; err != nil {
+			return nil, err
+		}
+	case "sqlite":
+		if err := c.db.Table("sqlite_schema").Where("type = 'table' AND name NOT LIKE 'sqlite_%'").Order("name").Pluck("name", &tableNames).Error; err != nil {
+			return nil, err
+		}
+	default:
+		return nil, status.Errorf(codes.Internal, "unsupported database %s", c.db.Name())
+	}
+	return tableNames, nil
+}
+
+func (c *Client) RowCount(tableName string) (int64, error) {
+	var count int64
+	err := c.db.Table(tableName).Count(&count).Error
+	return count, err
+}


Test patch:
diff --git a/gapic/admin_client_example_test.go b/gapic/admin_client_example_test.go
index ad806a5..be38621 100644
--- a/gapic/admin_client_example_test.go
+++ b/gapic/admin_client_example_test.go
@@ -1,4 +1,4 @@
-// Copyright 2021 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -57,6 +57,26 @@ func ExampleAdminClient_GetStatus() {
 	_ = resp
 }
 
+func ExampleAdminClient_GetStorage() {
+	ctx := context.Background()
+	c, err := gapic.NewAdminClient(ctx)
+	if err != nil {
+		// TODO: Handle error.
+	}
+	defer c.Close()
+
+	req := &emptypb.Empty{
+		// TODO: Fill request struct fields.
+		// See https://pkg.go.dev/google.golang.org/protobuf/types/known/emptypb#Empty.
+	}
+	resp, err := c.GetStorage(ctx, req)
+	if err != nil {
+		// TODO: Handle error.
+	}
+	// TODO: Use resp.
+	_ = resp
+}
+
 func ExampleAdminClient_MigrateDatabase() {
 	ctx := context.Background()
 	c, err := gapic.NewAdminClient(ctx)
diff --git a/gapic/registry_client_example_test.go b/gapic/registry_client_example_test.go
index a17a7a6..97efa42 100644
--- a/gapic/registry_client_example_test.go
+++ b/gapic/registry_client_example_test.go
@@ -1,4 +1,4 @@
-// Copyright 2021 Google LLC
+// Copyright 2022 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
diff --git a/server/registry/actions_storage_test.go b/server/registry/actions_storage_test.go
new file mode 100644
index 0000000..fb91255
--- /dev/null
+++ b/server/registry/actions_storage_test.go
@@ -0,0 +1,47 @@
+// Copyright 2022 Google LLC. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package registry
+
+import (
+	"context"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"google.golang.org/protobuf/testing/protocmp"
+	"google.golang.org/protobuf/types/known/emptypb"
+)
+
+func TestGetStorage(t *testing.T) {
+	ctx := context.Background()
+	server := defaultTestServer(t)
+
+	req := &emptypb.Empty{}
+
+	resp, err := server.GetStorage(ctx, req)
+	if err != nil {
+		t.Fatalf("GetStorage(%+v) returned error: %s", req, err)
+	}
+
+	// Ensure that we get the set of tables that we expect.
+	// Tables should be returned in alphabetical order.
+	want := []string{"apis", "artifacts", "blobs", "deployment_revision_tags", "deployments", "projects", "spec_revision_tags", "specs", "versions"}
+	got := make([]string, 0)
+	for _, c := range resp.Collections {
+		got = append(got, c.Name)
+	}
+	if !cmp.Equal(want, got) {
+		t.Errorf("GetStorage(%+v) returned unexpected diff (-want +got):\n%s", req, cmp.Diff(want, got, protocmp.Transform()))
+	}
+}