From 00f496b1b27dbcbaa5a9e802a37ecae386bc03ec Mon Sep 17 00:00:00 2001 From: hsinhoyeh Date: Sun, 16 Aug 2026 09:11:57 +0800 Subject: [PATCH] feat(api): expose _updatedAt on document metadata Writing a document that already exists is an UPSERT - CreateDocument with an existing documentId replaces the payload, and there is no Update rpc. The metadata returned to callers carried only createdAt and deletedAt, so an overwritten document was indistinguishable from one written once, in content and in metadata alike. A caller could not tell that its data had been replaced, or when. NOT A NEW COLUMN, and not a migration. ModelDocument has carried updated_at all along, and Write's OnConflict already lists it in DoUpdates while deliberately omitting created_at: DoUpdates: clause.AssignmentColumns([]string{"updated_at", "deleted_at", "data"}) So the database has been recording this correctly the whole time and only the API was hiding it. The fix is one proto field and one line of mapping. Non-optional, matching _createdAt rather than _deletedAt: gorm stamps updated_at on insert as well as on update, so every document has one, and on a document never rewritten it equals _createdAt. There is no such thing as a document without an updatedAt, so making it optional would invite callers to handle a case that cannot occur. Field 13; 12 was the last used. Generated output regenerated with the pinned tools (buf, and go-swagger v0.33.0 as gen-swagger-go-client.sh requires - it refuses to run against the v0.31.0 that was on PATH, which is why the pin exists). Tests come in two layers because they answer different questions: - dto_test.go asserts the MAPPING, including that a rewritten document and a fresh one are now distinguishable through the API, and that updatedAt survives alongside deletedAt rather than one overwriting the other. - TestUpsertAdvancesUpdatedAtAndPreservesCreatedAt asserts the BEHAVIOUR against real postgres: the second write advances updated_at, leaves created_at alone, replaces the payload, and does not append a row. This pins what makes the new field meaningful - if created_at ever joined the DoUpdates set, _updatedAt would still be populated and would still tell a caller nothing. Refs FootprintAI/grandturks#1125 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BSzNfcVc1FnDCfk9AF68Eh --- api/go-openapiv2/models/api_data_metadata.go | 34 + api/openapiv2/restcol.swagger.json | 5 + api/pb/restcol.pb.go | 684 ++++++++++--------- api/restcol.proto | 17 + pkg/models/documents/dto.go | 6 + pkg/models/documents/dto_test.go | 96 +++ pkg/storage/documents/documents_test.go | 69 ++ 7 files changed, 583 insertions(+), 328 deletions(-) create mode 100644 pkg/models/documents/dto_test.go diff --git a/api/go-openapiv2/models/api_data_metadata.go b/api/go-openapiv2/models/api_data_metadata.go index 75ee98e..ed75195 100644 --- a/api/go-openapiv2/models/api_data_metadata.go +++ b/api/go-openapiv2/models/api_data_metadata.go @@ -28,6 +28,24 @@ type APIDataMetadata struct { // Format: date-time DeletedAt strfmt.DateTime `json:"DeletedAt,omitempty"` + // ts when the record was last written. + // + // Writing a document that already exists is an UPSERT - CreateDocument with + // an existing documentId replaces the payload - and there is no separate + // Update rpc. Without this field an overwritten document is + // indistinguishable from one written once, in both content and metadata, so + // a caller cannot tell that its data was replaced or when. + // + // NOT a new column: ModelDocument has carried `updated_at` all along and + // gorm has been maintaining it on every write. This only surfaces what the + // database already records. + // + // Non-optional, matching _createdAt rather than _deletedAt: gorm sets + // updated_at on create as well as on update, so every document has one. On + // a document that has never been rewritten it equals _createdAt. + // Format: date-time + UpdatedAt strfmt.DateTime `json:"UpdatedAt,omitempty"` + // collectionId is collection id from collection data CollectionID string `json:"collectionId,omitempty"` @@ -56,6 +74,10 @@ func (m *APIDataMetadata) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := m.validateUpdatedAt(formats); err != nil { + res = append(res, err) + } + if err := m.validateDataformat(formats); err != nil { res = append(res, err) } @@ -90,6 +112,18 @@ func (m *APIDataMetadata) validateDeletedAt(formats strfmt.Registry) error { return nil } +func (m *APIDataMetadata) validateUpdatedAt(formats strfmt.Registry) error { + if swag.IsZero(m.UpdatedAt) { // not required + return nil + } + + if err := validate.FormatOf("UpdatedAt", "body", "date-time", m.UpdatedAt.String(), formats); err != nil { + return err + } + + return nil +} + func (m *APIDataMetadata) validateDataformat(formats strfmt.Registry) error { if swag.IsZero(m.Dataformat) { // not required return nil diff --git a/api/openapiv2/restcol.swagger.json b/api/openapiv2/restcol.swagger.json index ab27576..571e935 100644 --- a/api/openapiv2/restcol.swagger.json +++ b/api/openapiv2/restcol.swagger.json @@ -732,6 +732,11 @@ }, "dataformat": { "$ref": "#/definitions/apiDataFormat" + }, + "UpdatedAt": { + "type": "string", + "format": "date-time", + "description": "ts when the record was last written.\n\nWriting a document that already exists is an UPSERT - CreateDocument with\nan existing documentId replaces the payload - and there is no separate\nUpdate rpc. Without this field an overwritten document is\nindistinguishable from one written once, in both content and metadata, so\na caller cannot tell that its data was replaced or when.\n\nNOT a new column: ModelDocument has carried `updated_at` all along and\ngorm has been maintaining it on every write. This only surfaces what the\ndatabase already records.\n\nNon-optional, matching _createdAt rather than _deletedAt: gorm sets\nupdated_at on create as well as on update, so every document has one. On\na document that has never been rewritten it equals _createdAt." } } }, diff --git a/api/pb/restcol.pb.go b/api/pb/restcol.pb.go index 8468cdc..72f9364 100644 --- a/api/pb/restcol.pb.go +++ b/api/pb/restcol.pb.go @@ -880,6 +880,22 @@ type DataMetadata struct { XCreatedAt *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=_createdAt,json=CreatedAt,proto3" json:"_createdAt,omitempty"` // ts when the record was created XDeletedAt *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=_deletedAt,json=DeletedAt,proto3,oneof" json:"_deletedAt,omitempty"` // ts when the record was deleted Dataformat *DataFormat `protobuf:"varint,12,opt,name=dataformat,proto3,enum=restcol.api.DataFormat,oneof" json:"dataformat,omitempty"` + // ts when the record was last written. + // + // Writing a document that already exists is an UPSERT - CreateDocument with + // an existing documentId replaces the payload - and there is no separate + // Update rpc. Without this field an overwritten document is + // indistinguishable from one written once, in both content and metadata, so + // a caller cannot tell that its data was replaced or when. + // + // NOT a new column: ModelDocument has carried `updated_at` all along and + // gorm has been maintaining it on every write. This only surfaces what the + // database already records. + // + // Non-optional, matching _createdAt rather than _deletedAt: gorm sets + // updated_at on create as well as on update, so every document has one. On + // a document that has never been rewritten it equals _createdAt. + XUpdatedAt *timestamppb.Timestamp `protobuf:"bytes,13,opt,name=_updatedAt,json=UpdatedAt,proto3" json:"_updatedAt,omitempty"` } func (x *DataMetadata) Reset() { @@ -963,6 +979,13 @@ func (x *DataMetadata) GetDataformat() DataFormat { return DataFormat_DATA_FORMAT_UNKNOWN } +func (x *DataMetadata) GetXUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.XUpdatedAt + } + return nil +} + type CreateDocumentRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1662,7 +1685,7 @@ var file_restcol_proto_rawDesc = []byte{ 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0xe3, 0x02, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x73, 0x65, 0x22, 0x9e, 0x03, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, @@ -1682,316 +1705,320 @@ var file_restcol_proto_rawDesc = []byte{ 0x64, 0x61, 0x74, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x48, 0x01, 0x52, 0x0a, 0x64, 0x61, 0x74, - 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x58, 0x5f, - 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x61, - 0x74, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0xee, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, - 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, - 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x6f, 0x63, 0x75, - 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3c, 0x0a, - 0x0a, 0x64, 0x61, 0x74, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x17, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x48, 0x01, 0x52, 0x0a, 0x64, 0x61, - 0x74, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, - 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, - 0x61, 0x74, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x50, 0x0a, 0x16, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x09, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x52, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x9e, 0x01, 0x0a, 0x12, - 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, - 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, - 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, - 0x6e, 0x74, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x79, 0x0a, 0x13, - 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x09, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x52, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x79, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x22, - 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, - 0x49, 0x64, 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, - 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf1, 0x02, 0x0a, - 0x1a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, + 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x0a, 0x5f, 0x75, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x41, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x58, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x22, 0xee, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, + 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x63, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, + 0x23, 0x0a, 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, + 0x64, 0x88, 0x01, 0x01, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3c, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x72, + 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x46, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x48, 0x01, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x6f, 0x63, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x49, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x22, 0x50, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, + 0x0a, 0x09, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x9e, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x44, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, + 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x63, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, + 0x1e, 0x0a, 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, + 0x26, 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x79, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x44, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, + 0x0a, 0x09, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x44, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x22, 0x79, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, - 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x54, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, - 0x65, 0x54, 0x73, 0x12, 0x39, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x41, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x48, 0x00, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x12, 0x26, - 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x27, 0x0a, 0x0c, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, - 0x55, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x0c, - 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x55, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, - 0x23, 0x0a, 0x0a, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, + 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, + 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x18, 0x0a, + 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf1, 0x02, 0x0a, 0x1a, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x07, 0x73, 0x69, 0x6e, 0x63, + 0x65, 0x54, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x54, 0x73, 0x12, 0x39, + 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x07, 0x65, + 0x6e, 0x64, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0e, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x73, 0x12, 0x27, 0x0a, 0x0c, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x55, 0x70, 0x4d, 0x6f, 0x64, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x0c, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, + 0x77, 0x55, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x48, 0x02, + 0x52, 0x0a, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x42, + 0x0a, 0x0a, 0x08, 0x5f, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x41, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, + 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x55, 0x70, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x0d, 0x0a, 0x0b, + 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xb1, 0x02, 0x0a, 0x14, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x54, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x54, 0x73, 0x12, 0x39, 0x0a, 0x07, + 0x65, 0x6e, 0x64, 0x65, 0x64, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6e, 0x64, + 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x12, + 0x23, 0x0a, 0x0a, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x0a, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x41, 0x74, - 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x55, 0x70, 0x4d, 0x6f, 0x64, - 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, - 0x22, 0xb1, 0x02, 0x0a, 0x14, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, - 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x07, 0x73, - 0x69, 0x6e, 0x63, 0x65, 0x54, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x07, 0x73, 0x69, 0x6e, 0x63, 0x65, 0x54, - 0x73, 0x12, 0x39, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x41, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, - 0x52, 0x07, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0e, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0a, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x0a, 0x6c, 0x69, 0x6d, 0x69, - 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x65, 0x6e, - 0x64, 0x65, 0x64, 0x41, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x4d, 0x0a, 0x15, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, - 0x04, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, - 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, 0x64, - 0x6f, 0x63, 0x73, 0x2a, 0x86, 0x02, 0x0a, 0x13, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x53, - 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x44, 0x41, 0x54, 0x41, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, - 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x44, 0x41, 0x54, - 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, - 0x21, 0x0a, 0x1d, 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, - 0x44, 0x41, 0x54, 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, - 0x10, 0x02, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, 0x46, 0x49, 0x45, - 0x4c, 0x44, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, - 0x45, 0x47, 0x45, 0x52, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, + 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, + 0x4d, 0x0a, 0x15, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x04, 0x64, 0x6f, 0x63, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x04, 0x64, 0x6f, 0x63, 0x73, 0x2a, 0x86, + 0x02, 0x0a, 0x13, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x61, + 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x42, 0x4f, 0x4f, 0x4c, 0x10, 0x04, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x43, 0x48, 0x45, 0x4d, + 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x05, 0x12, 0x20, 0x0a, 0x1c, 0x53, 0x43, + 0x45, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59, 0x10, 0x06, 0x2a, 0xc5, 0x01, 0x0a, - 0x0e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x18, 0x0a, 0x14, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4f, 0x4c, - 0x4c, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x47, - 0x55, 0x4c, 0x41, 0x52, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x53, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, - 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x54, 0x49, 0x4d, 0x45, 0x53, 0x45, 0x52, 0x49, 0x45, 0x53, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, - 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x54, 0x52, 0x41, 0x4e, 0x53, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x1a, 0x0a, - 0x16, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x56, 0x45, 0x43, 0x54, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x4f, 0x4c, - 0x4c, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x52, 0x4f, - 0x58, 0x59, 0x10, 0x05, 0x2a, 0xa7, 0x01, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, - 0x6d, 0x61, 0x74, 0x12, 0x17, 0x0a, 0x13, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, - 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, - 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x41, 0x55, 0x54, 0x4f, - 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, - 0x54, 0x5f, 0x4a, 0x53, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x41, 0x54, 0x41, - 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x43, 0x53, 0x56, 0x10, 0x03, 0x12, 0x13, 0x0a, - 0x0f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x58, 0x4d, 0x4c, - 0x10, 0x04, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, - 0x54, 0x5f, 0x55, 0x52, 0x4c, 0x10, 0x05, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x41, 0x54, 0x41, 0x5f, - 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x10, 0x06, 0x32, 0xc2, - 0x12, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0xd7, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, - 0x44, 0x6f, 0x63, 0x12, 0x21, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x44, 0x6f, 0x63, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x8c, 0x01, 0x92, - 0x41, 0x24, 0x0a, 0x07, 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x12, 0x19, 0x52, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x20, 0x41, 0x50, 0x49, 0x20, 0x44, 0x6f, 0x63, 0x20, 0x69, 0x6e, 0x20, 0x53, - 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5f, 0x5a, 0x3c, 0x12, 0x3a, - 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x64, 0x7d, 0x2f, 0x61, 0x70, 0x69, 0x64, 0x6f, 0x63, 0x12, 0x1f, 0x2f, 0x76, 0x31, 0x2f, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x61, 0x70, 0x69, 0x64, 0x6f, 0x63, 0x12, 0xef, 0x01, 0x0a, 0x10, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x02, 0x12, 0x22, 0x0a, + 0x1e, 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x44, 0x41, + 0x54, 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x10, + 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, 0x46, 0x49, 0x45, 0x4c, + 0x44, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x4f, 0x4f, 0x4c, + 0x10, 0x04, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, 0x46, 0x49, 0x45, + 0x4c, 0x44, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x42, 0x4a, + 0x45, 0x43, 0x54, 0x10, 0x05, 0x12, 0x20, 0x0a, 0x1c, 0x53, 0x43, 0x48, 0x45, 0x4d, 0x41, 0x5f, + 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x41, 0x52, 0x52, 0x41, 0x59, 0x10, 0x06, 0x2a, 0xc5, 0x01, 0x0a, 0x0e, 0x43, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4f, + 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x4f, + 0x4e, 0x45, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x52, 0x45, 0x47, 0x55, 0x4c, 0x41, 0x52, 0x5f, + 0x46, 0x49, 0x4c, 0x45, 0x53, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x43, 0x4f, 0x4c, 0x4c, 0x45, + 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x49, 0x4d, 0x45, 0x53, + 0x45, 0x52, 0x49, 0x45, 0x53, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x43, 0x4f, 0x4c, 0x4c, 0x45, + 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x52, 0x41, 0x4e, 0x53, + 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4c, 0x4c, + 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x56, 0x45, 0x43, 0x54, + 0x4f, 0x52, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x10, 0x05, 0x2a, + 0xa7, 0x01, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x17, + 0x0a, 0x13, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x44, 0x41, 0x54, 0x41, 0x5f, + 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x10, 0x01, 0x12, 0x14, 0x0a, + 0x10, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x4a, 0x53, 0x4f, + 0x4e, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, + 0x41, 0x54, 0x5f, 0x43, 0x53, 0x56, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x44, 0x41, 0x54, 0x41, + 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x58, 0x4d, 0x4c, 0x10, 0x04, 0x12, 0x13, 0x0a, + 0x0f, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x55, 0x52, 0x4c, + 0x10, 0x05, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, + 0x54, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x10, 0x06, 0x32, 0xc2, 0x12, 0x0a, 0x0e, 0x52, 0x65, + 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xd7, 0x01, 0x0a, + 0x0d, 0x47, 0x65, 0x74, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x44, 0x6f, 0x63, 0x12, 0x21, + 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x44, 0x6f, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x48, + 0x74, 0x74, 0x70, 0x42, 0x6f, 0x64, 0x79, 0x22, 0x8c, 0x01, 0x92, 0x41, 0x24, 0x0a, 0x07, 0x73, + 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x12, 0x19, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x41, + 0x50, 0x49, 0x20, 0x44, 0x6f, 0x63, 0x20, 0x69, 0x6e, 0x20, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, + 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x5f, 0x5a, 0x3c, 0x12, 0x3a, 0x2f, 0x76, 0x31, 0x2f, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, + 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x2f, 0x61, + 0x70, 0x69, 0x64, 0x6f, 0x63, 0x12, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, + 0x61, 0x70, 0x69, 0x64, 0x6f, 0x63, 0x12, 0xef, 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x2e, 0x72, 0x65, + 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8d, 0x01, - 0x92, 0x41, 0x5b, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x4d, 0x41, 0x64, 0x64, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, - 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2c, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x65, 0x74, 0x20, - 0x6f, 0x66, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, - 0x68, 0x20, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x2d, 0x66, 0x72, 0x65, 0x65, 0x2e, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x29, 0x3a, 0x01, 0x2a, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, - 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xb4, 0x01, - 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8d, 0x01, 0x92, 0x41, 0x5b, 0x0a, 0x0a, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x41, 0x64, 0x64, 0x20, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x2c, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x64, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x73, 0x63, 0x68, + 0x65, 0x6d, 0x65, 0x2d, 0x66, 0x72, 0x65, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x3a, + 0x01, 0x2a, 0x22, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, + 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xb4, 0x01, 0x0a, 0x0f, 0x4c, 0x69, 0x73, + 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x2e, 0x72, + 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56, 0x92, 0x41, - 0x27, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x6c, - 0x69, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, - 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x12, 0x24, - 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xf7, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x74, - 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9e, 0x01, - 0x92, 0x41, 0x60, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, - 0x52, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x64, - 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, - 0x6e, 0x64, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x73, - 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x61, 0x69, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, - 0x20, 0x69, 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x12, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x70, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56, 0x92, 0x41, 0x27, 0x0a, 0x0a, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x63, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x12, 0x24, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, - 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x12, 0xe4, - 0x01, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x74, - 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x82, 0x01, 0x92, 0x41, 0x44, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x1a, 0x36, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, - 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73, 0x20, 0x61, 0x73, 0x73, 0x6f, - 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x20, 0x64, 0x6f, 0x63, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, - 0x2a, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x12, 0xf9, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, - 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x63, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, - 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x9d, 0x01, 0x92, 0x41, 0x2f, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x1a, 0x23, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x64, 0x6f, 0x63, 0x75, - 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x65, 0x3a, 0x01, 0x2a, 0x5a, - 0x3f, 0x3a, 0x01, 0x2a, 0x22, 0x3a, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, + 0xf7, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x21, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9e, 0x01, 0x92, 0x41, 0x60, 0x0a, 0x0a, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x52, 0x72, 0x65, 0x74, 0x72, + 0x69, 0x65, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, 0x75, + 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6b, 0x65, 0x79, 0x73, 0x20, 0x61, 0x73, 0x73, 0x6f, + 0x63, 0x61, 0x69, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x69, 0x74, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x35, 0x12, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x3a, 0x6e, 0x65, 0x77, 0x64, 0x6f, 0x63, - 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x6e, 0x65, 0x77, 0x64, 0x6f, - 0x63, 0x12, 0xe3, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x1f, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x90, 0x01, 0x92, 0x41, 0x40, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, - 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x34, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x61, - 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, - 0x12, 0x45, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x2f, 0x7b, 0x64, 0x6f, 0x63, 0x75, - 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x7d, 0x12, 0xe8, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x72, 0x65, 0x73, - 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, - 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x12, 0xe4, 0x01, 0x0a, 0x10, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x8c, 0x01, 0x92, 0x41, 0x3c, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, - 0x65, 0x6e, 0x74, 0x1a, 0x30, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, - 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x2a, 0x45, 0x2f, 0x76, 0x31, + 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x82, 0x01, 0x92, 0x41, + 0x44, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x36, 0x72, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x69, 0x6e, 0x64, 0x69, 0x76, 0x69, 0x64, + 0x75, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x69, 0x74, 0x73, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, + 0x64, 0x20, 0x64, 0x6f, 0x63, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x2a, 0x33, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, - 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x2f, 0x7b, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, - 0x64, 0x7d, 0x12, 0x91, 0x02, 0x0a, 0x14, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x27, 0x2e, 0x72, 0x65, - 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, - 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xab, 0x01, 0x92, 0x41, 0x61, 0x0a, 0x08, 0x64, 0x6f, - 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x55, 0x72, 0x75, 0x6e, 0x20, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x73, 0x74, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, - 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x6d, 0x61, 0x74, 0x63, - 0x68, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x41, 0x12, 0x3f, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x3a, 0x73, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x30, 0x01, 0x12, 0xea, 0x01, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x21, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, - 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, - 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x72, 0x65, - 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, + 0x12, 0xf9, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, + 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9d, 0x01, 0x92, + 0x41, 0x2f, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x23, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, + 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x65, 0x3a, 0x01, 0x2a, 0x5a, 0x3f, 0x3a, 0x01, 0x2a, 0x22, + 0x3a, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x64, 0x7d, 0x3a, 0x6e, 0x65, 0x77, 0x64, 0x6f, 0x63, 0x22, 0x1f, 0x2f, 0x76, 0x31, + 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x6e, 0x65, 0x77, 0x64, 0x6f, 0x63, 0x12, 0xe3, 0x01, 0x0a, + 0x0b, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1f, 0x2e, 0x72, + 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, + 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x91, 0x01, 0x92, 0x41, 0x4e, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x1a, - 0x42, 0x72, 0x75, 0x6e, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, - 0x73, 0x74, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, - 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3a, 0x12, 0x38, 0x2f, 0x76, 0x31, 0x2f, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, - 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x2f, 0x64, - 0x6f, 0x63, 0x73, 0x42, 0x80, 0x03, 0x92, 0x41, 0xd5, 0x02, 0x12, 0xc1, 0x01, 0x0a, 0x1a, 0x52, - 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x20, 0x41, 0x50, 0x49, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, - 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x44, 0x0a, 0x1a, 0x52, 0x65, 0x73, - 0x74, 0x43, 0x6f, 0x6c, 0x20, 0x41, 0x50, 0x49, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, - 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x6f, 0x74, - 0x70, 0x72, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2a, - 0x58, 0x0a, 0x14, 0x42, 0x53, 0x44, 0x20, 0x33, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20, - 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, - 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x6f, 0x74, - 0x70, 0x72, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x2f, 0x67, 0x72, 0x61, 0x6e, 0x64, 0x74, 0x75, 0x72, - 0x6b, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, - 0x43, 0x45, 0x4e, 0x53, 0x45, 0x2e, 0x74, 0x78, 0x74, 0x32, 0x03, 0x31, 0x2e, 0x30, 0x2a, 0x01, - 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, - 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x5a, 0x23, 0x0a, 0x21, 0x0a, 0x0a, 0x41, 0x70, 0x69, 0x4b, 0x65, - 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x13, 0x08, 0x02, 0x1a, 0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x02, 0x62, 0x10, 0x0a, 0x0e, 0x0a, 0x0a, - 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x00, 0x72, 0x31, 0x0a, 0x07, - 0x72, 0x65, 0x73, 0x74, 0x61, 0x70, 0x69, 0x12, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, - 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x6f, 0x74, - 0x70, 0x72, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x5a, - 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x6f, 0x74, - 0x70, 0x72, 0x69, 0x6e, 0x74, 0x61, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2f, - 0x61, 0x70, 0x69, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x90, 0x01, 0x92, 0x41, 0x40, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x1a, + 0x34, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x20, 0x61, 0x20, 0x64, 0x6f, 0x63, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x12, 0x45, 0x2f, 0x76, 0x31, + 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, + 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x2f, 0x7b, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, + 0x64, 0x7d, 0x12, 0xe8, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x63, + 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x74, + 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8c, + 0x01, 0x92, 0x41, 0x3c, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x30, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, + 0x66, 0x69, 0x63, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x66, 0x72, 0x6f, + 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x47, 0x2a, 0x45, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, + 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x2f, 0x64, 0x6f, 0x63, 0x73, + 0x2f, 0x7b, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x7d, 0x12, 0x91, 0x02, + 0x0a, 0x14, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x27, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x20, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, + 0x74, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0xab, 0x01, 0x92, 0x41, 0x61, 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x1a, 0x55, 0x72, 0x75, 0x6e, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, + 0x69, 0x6e, 0x73, 0x74, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x2c, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, + 0x20, 0x77, 0x68, 0x69, 0x63, 0x68, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x41, 0x12, 0x3f, + 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x7d, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x3a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x30, + 0x01, 0x12, 0xea, 0x01, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x21, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x91, 0x01, 0x92, 0x41, 0x4e, + 0x0a, 0x08, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x42, 0x72, 0x75, 0x6e, 0x20, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x73, 0x74, 0x20, 0x61, 0x20, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x72, 0x65, 0x74, 0x75, + 0x72, 0x6e, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x65, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x3a, 0x12, 0x38, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x73, 0x2f, 0x7b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x7d, 0x2f, 0x63, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6c, 0x6c, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x7d, 0x2f, 0x64, 0x6f, 0x63, 0x73, 0x42, 0x80, + 0x03, 0x92, 0x41, 0xd5, 0x02, 0x12, 0xc1, 0x01, 0x0a, 0x1a, 0x52, 0x65, 0x73, 0x74, 0x43, 0x6f, + 0x6c, 0x20, 0x41, 0x50, 0x49, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x44, 0x0a, 0x1a, 0x52, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6c, 0x20, + 0x41, 0x50, 0x49, 0x20, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x6f, 0x74, 0x70, 0x72, 0x69, 0x6e, 0x74, + 0x61, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2a, 0x58, 0x0a, 0x14, 0x42, 0x53, + 0x44, 0x20, 0x33, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, + 0x73, 0x65, 0x12, 0x40, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x6f, 0x74, 0x70, 0x72, 0x69, 0x6e, 0x74, + 0x61, 0x69, 0x2f, 0x67, 0x72, 0x61, 0x6e, 0x64, 0x74, 0x75, 0x72, 0x6b, 0x2f, 0x62, 0x6c, 0x6f, + 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, + 0x2e, 0x74, 0x78, 0x74, 0x32, 0x03, 0x31, 0x2e, 0x30, 0x2a, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, + 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, + 0x5a, 0x23, 0x0a, 0x21, 0x0a, 0x0a, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, + 0x12, 0x13, 0x08, 0x02, 0x1a, 0x0d, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x02, 0x62, 0x10, 0x0a, 0x0e, 0x0a, 0x0a, 0x41, 0x70, 0x69, 0x4b, 0x65, + 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, 0x00, 0x72, 0x31, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x74, 0x61, + 0x70, 0x69, 0x12, 0x26, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x6f, 0x74, 0x70, 0x72, 0x69, 0x6e, 0x74, + 0x61, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x6f, 0x6f, 0x74, 0x70, 0x72, 0x69, 0x6e, 0x74, + 0x61, 0x69, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x63, 0x6f, 0x6c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, + 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2053,40 +2080,41 @@ var file_restcol_proto_depIdxs = []int32{ 25, // 12: restcol.api.DataMetadata._createdAt:type_name -> google.protobuf.Timestamp 25, // 13: restcol.api.DataMetadata._deletedAt:type_name -> google.protobuf.Timestamp 2, // 14: restcol.api.DataMetadata.dataformat:type_name -> restcol.api.DataFormat - 2, // 15: restcol.api.CreateDocumentRequest.dataformat:type_name -> restcol.api.DataFormat - 14, // 16: restcol.api.CreateDocumentResponse._metadata:type_name -> restcol.api.DataMetadata - 14, // 17: restcol.api.GetDocumentResponse._metadata:type_name -> restcol.api.DataMetadata - 24, // 18: restcol.api.GetDocumentResponse.data:type_name -> google.protobuf.Value - 25, // 19: restcol.api.QueryDocumentStreamRequest.sinceTs:type_name -> google.protobuf.Timestamp - 25, // 20: restcol.api.QueryDocumentStreamRequest.endedAt:type_name -> google.protobuf.Timestamp - 25, // 21: restcol.api.QueryDocumentRequest.sinceTs:type_name -> google.protobuf.Timestamp - 25, // 22: restcol.api.QueryDocumentRequest.endedAt:type_name -> google.protobuf.Timestamp - 18, // 23: restcol.api.QueryDocumentResponse.docs:type_name -> restcol.api.GetDocumentResponse - 3, // 24: restcol.api.RestColService.GetSwaggerDoc:input_type -> restcol.api.GetSwaggerDocRequest - 6, // 25: restcol.api.RestColService.CreateCollection:input_type -> restcol.api.CreateCollectionRequest - 8, // 26: restcol.api.RestColService.ListCollections:input_type -> restcol.api.ListCollectionsRequest - 10, // 27: restcol.api.RestColService.GetCollection:input_type -> restcol.api.GetCollectionRequest - 12, // 28: restcol.api.RestColService.DeleteCollection:input_type -> restcol.api.DeleteCollectionRequest - 15, // 29: restcol.api.RestColService.CreateDocument:input_type -> restcol.api.CreateDocumentRequest - 17, // 30: restcol.api.RestColService.GetDocument:input_type -> restcol.api.GetDocumentRequest - 19, // 31: restcol.api.RestColService.DeleteDocument:input_type -> restcol.api.DeleteDocumentRequest - 21, // 32: restcol.api.RestColService.QueryDocumentsStream:input_type -> restcol.api.QueryDocumentStreamRequest - 22, // 33: restcol.api.RestColService.QueryDocument:input_type -> restcol.api.QueryDocumentRequest - 26, // 34: restcol.api.RestColService.GetSwaggerDoc:output_type -> google.api.HttpBody - 7, // 35: restcol.api.RestColService.CreateCollection:output_type -> restcol.api.CreateCollectionResponse - 9, // 36: restcol.api.RestColService.ListCollections:output_type -> restcol.api.ListCollectionsResponse - 11, // 37: restcol.api.RestColService.GetCollection:output_type -> restcol.api.GetCollectionResponse - 13, // 38: restcol.api.RestColService.DeleteCollection:output_type -> restcol.api.DeleteCollectionResponse - 16, // 39: restcol.api.RestColService.CreateDocument:output_type -> restcol.api.CreateDocumentResponse - 18, // 40: restcol.api.RestColService.GetDocument:output_type -> restcol.api.GetDocumentResponse - 20, // 41: restcol.api.RestColService.DeleteDocument:output_type -> restcol.api.DeleteDocumentResponse - 18, // 42: restcol.api.RestColService.QueryDocumentsStream:output_type -> restcol.api.GetDocumentResponse - 23, // 43: restcol.api.RestColService.QueryDocument:output_type -> restcol.api.QueryDocumentResponse - 34, // [34:44] is the sub-list for method output_type - 24, // [24:34] is the sub-list for method input_type - 24, // [24:24] is the sub-list for extension type_name - 24, // [24:24] is the sub-list for extension extendee - 0, // [0:24] is the sub-list for field type_name + 25, // 15: restcol.api.DataMetadata._updatedAt:type_name -> google.protobuf.Timestamp + 2, // 16: restcol.api.CreateDocumentRequest.dataformat:type_name -> restcol.api.DataFormat + 14, // 17: restcol.api.CreateDocumentResponse._metadata:type_name -> restcol.api.DataMetadata + 14, // 18: restcol.api.GetDocumentResponse._metadata:type_name -> restcol.api.DataMetadata + 24, // 19: restcol.api.GetDocumentResponse.data:type_name -> google.protobuf.Value + 25, // 20: restcol.api.QueryDocumentStreamRequest.sinceTs:type_name -> google.protobuf.Timestamp + 25, // 21: restcol.api.QueryDocumentStreamRequest.endedAt:type_name -> google.protobuf.Timestamp + 25, // 22: restcol.api.QueryDocumentRequest.sinceTs:type_name -> google.protobuf.Timestamp + 25, // 23: restcol.api.QueryDocumentRequest.endedAt:type_name -> google.protobuf.Timestamp + 18, // 24: restcol.api.QueryDocumentResponse.docs:type_name -> restcol.api.GetDocumentResponse + 3, // 25: restcol.api.RestColService.GetSwaggerDoc:input_type -> restcol.api.GetSwaggerDocRequest + 6, // 26: restcol.api.RestColService.CreateCollection:input_type -> restcol.api.CreateCollectionRequest + 8, // 27: restcol.api.RestColService.ListCollections:input_type -> restcol.api.ListCollectionsRequest + 10, // 28: restcol.api.RestColService.GetCollection:input_type -> restcol.api.GetCollectionRequest + 12, // 29: restcol.api.RestColService.DeleteCollection:input_type -> restcol.api.DeleteCollectionRequest + 15, // 30: restcol.api.RestColService.CreateDocument:input_type -> restcol.api.CreateDocumentRequest + 17, // 31: restcol.api.RestColService.GetDocument:input_type -> restcol.api.GetDocumentRequest + 19, // 32: restcol.api.RestColService.DeleteDocument:input_type -> restcol.api.DeleteDocumentRequest + 21, // 33: restcol.api.RestColService.QueryDocumentsStream:input_type -> restcol.api.QueryDocumentStreamRequest + 22, // 34: restcol.api.RestColService.QueryDocument:input_type -> restcol.api.QueryDocumentRequest + 26, // 35: restcol.api.RestColService.GetSwaggerDoc:output_type -> google.api.HttpBody + 7, // 36: restcol.api.RestColService.CreateCollection:output_type -> restcol.api.CreateCollectionResponse + 9, // 37: restcol.api.RestColService.ListCollections:output_type -> restcol.api.ListCollectionsResponse + 11, // 38: restcol.api.RestColService.GetCollection:output_type -> restcol.api.GetCollectionResponse + 13, // 39: restcol.api.RestColService.DeleteCollection:output_type -> restcol.api.DeleteCollectionResponse + 16, // 40: restcol.api.RestColService.CreateDocument:output_type -> restcol.api.CreateDocumentResponse + 18, // 41: restcol.api.RestColService.GetDocument:output_type -> restcol.api.GetDocumentResponse + 20, // 42: restcol.api.RestColService.DeleteDocument:output_type -> restcol.api.DeleteDocumentResponse + 18, // 43: restcol.api.RestColService.QueryDocumentsStream:output_type -> restcol.api.GetDocumentResponse + 23, // 44: restcol.api.RestColService.QueryDocument:output_type -> restcol.api.QueryDocumentResponse + 35, // [35:45] is the sub-list for method output_type + 25, // [25:35] is the sub-list for method input_type + 25, // [25:25] is the sub-list for extension type_name + 25, // [25:25] is the sub-list for extension extendee + 0, // [0:25] is the sub-list for field type_name } func init() { file_restcol_proto_init() } diff --git a/api/restcol.proto b/api/restcol.proto index f5263b2..f595b07 100644 --- a/api/restcol.proto +++ b/api/restcol.proto @@ -282,6 +282,23 @@ message DataMetadata { google.protobuf.Timestamp _createdAt = 10; // ts when the record was created optional google.protobuf.Timestamp _deletedAt = 11; // ts when the record was deleted optional DataFormat dataformat = 12; + + // ts when the record was last written. + // + // Writing a document that already exists is an UPSERT - CreateDocument with + // an existing documentId replaces the payload - and there is no separate + // Update rpc. Without this field an overwritten document is + // indistinguishable from one written once, in both content and metadata, so + // a caller cannot tell that its data was replaced or when. + // + // NOT a new column: ModelDocument has carried `updated_at` all along and + // gorm has been maintaining it on every write. This only surfaces what the + // database already records. + // + // Non-optional, matching _createdAt rather than _deletedAt: gorm sets + // updated_at on create as well as on update, so every document has one. On + // a document that has never been rewritten it equals _createdAt. + google.protobuf.Timestamp _updatedAt = 13; } diff --git a/pkg/models/documents/dto.go b/pkg/models/documents/dto.go index b8f63aa..bd03463 100644 --- a/pkg/models/documents/dto.go +++ b/pkg/models/documents/dto.go @@ -21,6 +21,12 @@ func NewPbDocumentMetadata(md *ModelDocument) *apppb.DataMetadata { //Dataformat: nil; // FIXME(hsiny): need to fix dataformat XCreatedAt: timestamppb.New(md.CreatedAt), + // Surfaced so a caller can tell an overwritten document from one written + // once. Writing an existing documentId is an upsert and there is no + // Update rpc, so before this the two were indistinguishable in both + // content and metadata. gorm has always maintained the column; only the + // API was hiding it. + XUpdatedAt: timestamppb.New(md.UpdatedAt), XDeletedAt: nil, } if deletedAt, _ := md.DeletedAt.Value(); deletedAt != nil { diff --git a/pkg/models/documents/dto_test.go b/pkg/models/documents/dto_test.go new file mode 100644 index 0000000..3145239 --- /dev/null +++ b/pkg/models/documents/dto_test.go @@ -0,0 +1,96 @@ +package modeldocuments + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "gorm.io/gorm" + + modelcollections "github.com/footprintai/restcol/pkg/models/collections" + modelprojects "github.com/footprintai/restcol/pkg/models/projects" +) + +// The metadata a caller sees has to say when a document was last written. +// +// Writing an existing documentId is an UPSERT - there is no Update rpc - so +// without _updatedAt an overwritten document is indistinguishable from one +// written once, in content and in metadata alike. A caller cannot tell that its +// data was replaced, or when. +// +// The column was never missing: ModelDocument has always carried updated_at and +// the upsert in DocumentCURD.Write lists it in DoUpdates. Only the API hid it, +// which is why this is a mapping fix rather than a migration. + +func fixture(created, updated time.Time) *ModelDocument { + return &ModelDocument{ + ID: NewDocumentID(), + CreatedAt: created, + UpdatedAt: updated, + Data: NewModelDocumentData(map[string]interface{}{"k": "v"}), + ModelCollectionID: modelcollections.CollectionID("col-1"), + ModelProjectID: modelprojects.ProjectID("proj-1"), + } +} + +func TestUpdatedAtIsSurfaced(t *testing.T) { + created := time.Date(2026, 8, 1, 10, 0, 0, 0, time.UTC) + updated := time.Date(2026, 8, 9, 17, 30, 0, 0, time.UTC) + + md := NewPbDocumentMetadata(fixture(created, updated)) + + require.NotNil(t, md.XUpdatedAt, "_updatedAt must be populated; a nil here is the gap this fixes") + assert.Equal(t, updated.UTC(), md.XUpdatedAt.AsTime().UTC()) + assert.Equal(t, created.UTC(), md.XCreatedAt.AsTime().UTC(), + "_createdAt must not be overwritten by the new field") +} + +// The distinguishing case, and the whole point: two documents that differ ONLY +// in having been rewritten must be distinguishable through the API. +func TestARewrittenDocumentIsDistinguishableFromAFreshOne(t *testing.T) { + created := time.Date(2026, 8, 1, 10, 0, 0, 0, time.UTC) + + fresh := NewPbDocumentMetadata(fixture(created, created)) + rewritten := NewPbDocumentMetadata(fixture(created, created.Add(48*time.Hour))) + + assert.Equal(t, fresh.XCreatedAt.AsTime(), rewritten.XCreatedAt.AsTime(), + "createdAt is identical - it is the only thing these two shared before") + assert.True(t, + rewritten.XUpdatedAt.AsTime().After(fresh.XUpdatedAt.AsTime()), + "the rewritten document must report a later _updatedAt; if these compare "+ + "equal the metadata still cannot tell an overwrite from a first write") +} + +// On a document nobody has rewritten, updatedAt equals createdAt - gorm sets +// both on insert. Pinned because it justifies the field being non-optional: +// there is no such thing as a document without an updatedAt. +func TestNeverRewrittenReportsUpdatedEqualToCreated(t *testing.T) { + at := time.Date(2026, 8, 1, 10, 0, 0, 0, time.UTC) + + md := NewPbDocumentMetadata(fixture(at, at)) + + require.NotNil(t, md.XUpdatedAt) + assert.Equal(t, md.XCreatedAt.AsTime(), md.XUpdatedAt.AsTime()) +} + +// A soft-deleted document still reports when it was last written. The three +// timestamps are independent and must not overwrite one another - an earlier +// version of this mapping set XDeletedAt to nil unconditionally and then +// populated it, which is exactly the shape that loses a field silently. +func TestDeletedAtAndUpdatedAtCoexist(t *testing.T) { + created := time.Date(2026, 8, 1, 10, 0, 0, 0, time.UTC) + updated := created.Add(time.Hour) + deleted := created.Add(2 * time.Hour) + + md := fixture(created, updated) + md.DeletedAt = gorm.DeletedAt{Time: deleted, Valid: true} + + pb := NewPbDocumentMetadata(md) + + require.NotNil(t, pb.XDeletedAt, "deletedAt must still be populated") + require.NotNil(t, pb.XUpdatedAt, "updatedAt must survive alongside deletedAt") + assert.Equal(t, deleted.UTC(), pb.XDeletedAt.AsTime().UTC()) + assert.Equal(t, updated.UTC(), pb.XUpdatedAt.AsTime().UTC()) + assert.Equal(t, created.UTC(), pb.XCreatedAt.AsTime().UTC()) +} diff --git a/pkg/storage/documents/documents_test.go b/pkg/storage/documents/documents_test.go index 0ec3ec5..1aad3cf 100644 --- a/pkg/storage/documents/documents_test.go +++ b/pkg/storage/documents/documents_test.go @@ -337,3 +337,72 @@ func TestDocumentCURD_DeleteByCollection_IsPermanent(t *testing.T) { Count(&remaining).Error) assert.Zero(t, remaining, "the cascade must remove the rows, not tombstone them") } + +// Writing an existing documentId is an UPSERT - there is no Update rpc - and +// the metadata has to record that it happened. Before FootprintAI/restcol#141 +// the API exposed only createdAt and deletedAt, so an overwritten document was +// indistinguishable from one written once. +// +// The storage layer was already correct: Write's OnConflict lists updated_at in +// DoUpdates and deliberately omits created_at. This pins that behaviour, because +// the new API field is only meaningful if these two move independently - if +// created_at ever joined the DoUpdates set, _updatedAt would still be populated +// and still tell a caller nothing. +func TestUpsertAdvancesUpdatedAtAndPreservesCreatedAt(t *testing.T) { + if testing.Short() { + t.Skip("needs postgres") + return + } + ctx := context.Background() + postgrescli, err := storagetestutils.NewTestPostgresCli(logger.NewLogger(false)) + assert.NoError(t, err) + + regularProject, _, err := storageprojects.TestProjectSuite(postgrescli) + assert.Nil(t, err) + collection, err := storagecollectionstestutils.TestCollectionSuite(postgrescli, regularProject) + assert.NoError(t, err) + + dcrud := NewDocumentCURD(postgrescli) + assert.Nil(t, dcrud.AutoMigrate()) + + docID := appmodeldocuments.NewDocumentID() + write := func(v string) { + assert.NoError(t, dcrud.Write(ctx, "", &appmodeldocuments.ModelDocument{ + ID: docID, + Data: appmodeldocuments.NewModelDocumentData(map[string]interface{}{"v": v}), + ModelCollectionID: collection.ID, + ModelProjectID: regularProject.ID, + })) + } + + write("first") + first, err := dcrud.Get(ctx, "", regularProject.ID, collection.ID, docID) + assert.NoError(t, err) + + // A fresh row: gorm stamps both columns on insert, which is what makes + // _updatedAt safe to expose as non-optional. + assert.False(t, first.UpdatedAt.IsZero(), "updated_at must be set on insert") + assert.WithinDuration(t, first.CreatedAt, first.UpdatedAt, time.Second, + "on a document written once, updated_at should match created_at") + + // Postgres stores microseconds, so without a pause the two writes can land + // on the same instant and the assertion below would pass or fail on timing + // rather than on behaviour. + time.Sleep(10 * time.Millisecond) + + write("second") + second, err := dcrud.Get(ctx, "", regularProject.ID, collection.ID, docID) + assert.NoError(t, err) + + assert.True(t, second.UpdatedAt.After(first.UpdatedAt), + "the upsert must advance updated_at; without it an overwrite leaves no trace") + assert.WithinDuration(t, first.CreatedAt, second.CreatedAt, time.Millisecond, + "created_at must survive the upsert - it is what _updatedAt is compared against") + + // And the upsert really did replace the payload rather than insert a second + // row, which is the premise the timestamps are describing. + assert.Equal(t, "second", second.Data.MapValue["v"]) + count, err := dcrud.CountByCollection(ctx, "", regularProject.ID, collection.ID) + assert.NoError(t, err) + assert.EqualValues(t, 1, count, "upsert must not append a second row") +}