From 498abdda0ee6315818ffbd38c5168fe608ec199b Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Tue, 6 Aug 2024 14:17:59 +0700 Subject: [PATCH 01/45] feat(proto): generate proto regarding delete assets --- Makefile | 2 +- proto/compass.swagger.yaml | 53 + .../gotocompany/compass/v1beta1/service.pb.go | 3480 +++++++++-------- .../compass/v1beta1/service.pb.gw.go | 85 + .../compass/v1beta1/service.pb.validate.go | 219 ++ .../compass/v1beta1/service_grpc.pb.go | 37 + 6 files changed, 2219 insertions(+), 1657 deletions(-) diff --git a/Makefile b/Makefile index b678de2e..428c30d4 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ help: ##@help show this help NAME="github.com/goto/compass" VERSION=$(shell git describe --always --tags 2>/dev/null) COVERFILE="/tmp/compass.coverprofile" -PROTON_COMMIT := "fe99cc96e060085d6052096e9ba59b4038c691c6" +PROTON_COMMIT := "8375eddcb23d38f601f6036c676493d8feb84a7e" TOOLS_MOD_DIR = ./tools TOOLS_DIR = $(abspath ./.tools) diff --git a/proto/compass.swagger.yaml b/proto/compass.swagger.yaml index 69a09bc6..84c9cc49 100644 --- a/proto/compass.swagger.yaml +++ b/proto/compass.swagger.yaml @@ -420,6 +420,44 @@ paths: type: string tags: - Asset + /v1beta1/assets/delete-by-query: + post: + summary: Delete assets + description: Delete all assets that match with specified query expr + operationId: CompassService_DeleteAssets + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/DeleteAssetsResponse' + "400": + description: Returned when the data that user input is wrong. + schema: + $ref: '#/definitions/Status' + "404": + description: Returned when the resource does not exist. + schema: + $ref: '#/definitions/Status' + "409": + description: Returned when the resource already exist. + schema: + $ref: '#/definitions/Status' + "500": + description: Returned when theres is something wrong on the server side. + schema: + $ref: '#/definitions/Status' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/Status' + parameters: + - name: body + in: body + required: true + schema: + $ref: '#/definitions/DeleteAssetsRequest' + tags: + - Asset /v1beta1/assets/sync: post: summary: Syncs assets in elasticsearch @@ -2046,6 +2084,21 @@ definitions: $ref: '#/definitions/TagTemplate' DeleteAssetResponse: type: object + DeleteAssetsRequest: + type: object + properties: + query_expr: + type: string + description: 'filter by query expr, example: refreshed_at <= "2024-08-05T23:59:59"' + dry_run: + type: boolean + description: if set to true, then deletion should not proceed and only return the affected rows. else, will perform deletion in the background + DeleteAssetsResponse: + type: object + properties: + affected_rows: + type: integer + format: int64 DeleteCommentResponse: type: object DeleteTagAssetResponse: diff --git a/proto/gotocompany/compass/v1beta1/service.pb.go b/proto/gotocompany/compass/v1beta1/service.pb.go index aaef40e7..4690660d 100644 --- a/proto/gotocompany/compass/v1beta1/service.pb.go +++ b/proto/gotocompany/compass/v1beta1/service.pb.go @@ -2468,6 +2468,108 @@ func (*DeleteAssetResponse) Descriptor() ([]byte, []int) { return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{40} } +type DeleteAssetsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + QueryExpr string `protobuf:"bytes,1,opt,name=query_expr,json=queryExpr,proto3" json:"query_expr,omitempty"` + DryRun bool `protobuf:"varint,2,opt,name=dry_run,json=dryRun,proto3" json:"dry_run,omitempty"` +} + +func (x *DeleteAssetsRequest) Reset() { + *x = DeleteAssetsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteAssetsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAssetsRequest) ProtoMessage() {} + +func (x *DeleteAssetsRequest) ProtoReflect() protoreflect.Message { + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[41] + 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 DeleteAssetsRequest.ProtoReflect.Descriptor instead. +func (*DeleteAssetsRequest) Descriptor() ([]byte, []int) { + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{41} +} + +func (x *DeleteAssetsRequest) GetQueryExpr() string { + if x != nil { + return x.QueryExpr + } + return "" +} + +func (x *DeleteAssetsRequest) GetDryRun() bool { + if x != nil { + return x.DryRun + } + return false +} + +type DeleteAssetsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AffectedRows uint32 `protobuf:"varint,1,opt,name=affected_rows,json=affectedRows,proto3" json:"affected_rows,omitempty"` +} + +func (x *DeleteAssetsResponse) Reset() { + *x = DeleteAssetsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteAssetsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteAssetsResponse) ProtoMessage() {} + +func (x *DeleteAssetsResponse) ProtoReflect() protoreflect.Message { + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[42] + 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 DeleteAssetsResponse.ProtoReflect.Descriptor instead. +func (*DeleteAssetsResponse) Descriptor() ([]byte, []int) { + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{42} +} + +func (x *DeleteAssetsResponse) GetAffectedRows() uint32 { + if x != nil { + return x.AffectedRows + } + return 0 +} + type GetAssetStargazersRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2481,7 +2583,7 @@ type GetAssetStargazersRequest struct { func (x *GetAssetStargazersRequest) Reset() { *x = GetAssetStargazersRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[41] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2494,7 +2596,7 @@ func (x *GetAssetStargazersRequest) String() string { func (*GetAssetStargazersRequest) ProtoMessage() {} func (x *GetAssetStargazersRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[41] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[43] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2507,7 +2609,7 @@ func (x *GetAssetStargazersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAssetStargazersRequest.ProtoReflect.Descriptor instead. func (*GetAssetStargazersRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{41} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{43} } func (x *GetAssetStargazersRequest) GetId() string { @@ -2542,7 +2644,7 @@ type GetAssetStargazersResponse struct { func (x *GetAssetStargazersResponse) Reset() { *x = GetAssetStargazersResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[42] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2555,7 +2657,7 @@ func (x *GetAssetStargazersResponse) String() string { func (*GetAssetStargazersResponse) ProtoMessage() {} func (x *GetAssetStargazersResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[42] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[44] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2568,7 +2670,7 @@ func (x *GetAssetStargazersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAssetStargazersResponse.ProtoReflect.Descriptor instead. func (*GetAssetStargazersResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{42} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{44} } func (x *GetAssetStargazersResponse) GetData() []*User { @@ -2591,7 +2693,7 @@ type GetAssetVersionHistoryRequest struct { func (x *GetAssetVersionHistoryRequest) Reset() { *x = GetAssetVersionHistoryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[43] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2604,7 +2706,7 @@ func (x *GetAssetVersionHistoryRequest) String() string { func (*GetAssetVersionHistoryRequest) ProtoMessage() {} func (x *GetAssetVersionHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[43] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2617,7 +2719,7 @@ func (x *GetAssetVersionHistoryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAssetVersionHistoryRequest.ProtoReflect.Descriptor instead. func (*GetAssetVersionHistoryRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{43} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{45} } func (x *GetAssetVersionHistoryRequest) GetId() string { @@ -2652,7 +2754,7 @@ type GetAssetVersionHistoryResponse struct { func (x *GetAssetVersionHistoryResponse) Reset() { *x = GetAssetVersionHistoryResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[44] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2665,7 +2767,7 @@ func (x *GetAssetVersionHistoryResponse) String() string { func (*GetAssetVersionHistoryResponse) ProtoMessage() {} func (x *GetAssetVersionHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[44] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2678,7 +2780,7 @@ func (x *GetAssetVersionHistoryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAssetVersionHistoryResponse.ProtoReflect.Descriptor instead. func (*GetAssetVersionHistoryResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{44} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{46} } func (x *GetAssetVersionHistoryResponse) GetData() []*Asset { @@ -2700,7 +2802,7 @@ type GetAssetByVersionRequest struct { func (x *GetAssetByVersionRequest) Reset() { *x = GetAssetByVersionRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[45] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2713,7 +2815,7 @@ func (x *GetAssetByVersionRequest) String() string { func (*GetAssetByVersionRequest) ProtoMessage() {} func (x *GetAssetByVersionRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[45] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2726,7 +2828,7 @@ func (x *GetAssetByVersionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAssetByVersionRequest.ProtoReflect.Descriptor instead. func (*GetAssetByVersionRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{45} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{47} } func (x *GetAssetByVersionRequest) GetId() string { @@ -2754,7 +2856,7 @@ type GetAssetByVersionResponse struct { func (x *GetAssetByVersionResponse) Reset() { *x = GetAssetByVersionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[46] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2767,7 +2869,7 @@ func (x *GetAssetByVersionResponse) String() string { func (*GetAssetByVersionResponse) ProtoMessage() {} func (x *GetAssetByVersionResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[46] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2780,7 +2882,7 @@ func (x *GetAssetByVersionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAssetByVersionResponse.ProtoReflect.Descriptor instead. func (*GetAssetByVersionResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{46} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{48} } func (x *GetAssetByVersionResponse) GetData() *Asset { @@ -2802,7 +2904,7 @@ type CreateAssetProbeRequest struct { func (x *CreateAssetProbeRequest) Reset() { *x = CreateAssetProbeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[47] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2815,7 +2917,7 @@ func (x *CreateAssetProbeRequest) String() string { func (*CreateAssetProbeRequest) ProtoMessage() {} func (x *CreateAssetProbeRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[47] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2828,7 +2930,7 @@ func (x *CreateAssetProbeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateAssetProbeRequest.ProtoReflect.Descriptor instead. func (*CreateAssetProbeRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{47} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{49} } func (x *CreateAssetProbeRequest) GetAssetUrn() string { @@ -2856,7 +2958,7 @@ type CreateAssetProbeResponse struct { func (x *CreateAssetProbeResponse) Reset() { *x = CreateAssetProbeResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[48] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2869,7 +2971,7 @@ func (x *CreateAssetProbeResponse) String() string { func (*CreateAssetProbeResponse) ProtoMessage() {} func (x *CreateAssetProbeResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[48] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2882,7 +2984,7 @@ func (x *CreateAssetProbeResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateAssetProbeResponse.ProtoReflect.Descriptor instead. func (*CreateAssetProbeResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{48} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{50} } func (x *CreateAssetProbeResponse) GetId() string { @@ -2903,7 +3005,7 @@ type SyncAssetsRequest struct { func (x *SyncAssetsRequest) Reset() { *x = SyncAssetsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[49] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2916,7 +3018,7 @@ func (x *SyncAssetsRequest) String() string { func (*SyncAssetsRequest) ProtoMessage() {} func (x *SyncAssetsRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[49] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2929,7 +3031,7 @@ func (x *SyncAssetsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncAssetsRequest.ProtoReflect.Descriptor instead. func (*SyncAssetsRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{49} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{51} } func (x *SyncAssetsRequest) GetServices() []string { @@ -2948,7 +3050,7 @@ type SyncAssetsResponse struct { func (x *SyncAssetsResponse) Reset() { *x = SyncAssetsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[50] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2961,7 +3063,7 @@ func (x *SyncAssetsResponse) String() string { func (*SyncAssetsResponse) ProtoMessage() {} func (x *SyncAssetsResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[50] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2974,7 +3076,7 @@ func (x *SyncAssetsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SyncAssetsResponse.ProtoReflect.Descriptor instead. func (*SyncAssetsResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{50} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{52} } type GetUserStarredAssetsRequest struct { @@ -2990,7 +3092,7 @@ type GetUserStarredAssetsRequest struct { func (x *GetUserStarredAssetsRequest) Reset() { *x = GetUserStarredAssetsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[51] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3003,7 +3105,7 @@ func (x *GetUserStarredAssetsRequest) String() string { func (*GetUserStarredAssetsRequest) ProtoMessage() {} func (x *GetUserStarredAssetsRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[51] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3016,7 +3118,7 @@ func (x *GetUserStarredAssetsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserStarredAssetsRequest.ProtoReflect.Descriptor instead. func (*GetUserStarredAssetsRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{51} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{53} } func (x *GetUserStarredAssetsRequest) GetUserId() string { @@ -3051,7 +3153,7 @@ type GetUserStarredAssetsResponse struct { func (x *GetUserStarredAssetsResponse) Reset() { *x = GetUserStarredAssetsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[52] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3064,7 +3166,7 @@ func (x *GetUserStarredAssetsResponse) String() string { func (*GetUserStarredAssetsResponse) ProtoMessage() {} func (x *GetUserStarredAssetsResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[52] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3077,7 +3179,7 @@ func (x *GetUserStarredAssetsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetUserStarredAssetsResponse.ProtoReflect.Descriptor instead. func (*GetUserStarredAssetsResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{52} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{54} } func (x *GetUserStarredAssetsResponse) GetData() []*Asset { @@ -3099,7 +3201,7 @@ type GetMyStarredAssetsRequest struct { func (x *GetMyStarredAssetsRequest) Reset() { *x = GetMyStarredAssetsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[53] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3112,7 +3214,7 @@ func (x *GetMyStarredAssetsRequest) String() string { func (*GetMyStarredAssetsRequest) ProtoMessage() {} func (x *GetMyStarredAssetsRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[53] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3125,7 +3227,7 @@ func (x *GetMyStarredAssetsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyStarredAssetsRequest.ProtoReflect.Descriptor instead. func (*GetMyStarredAssetsRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{53} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{55} } func (x *GetMyStarredAssetsRequest) GetSize() uint32 { @@ -3153,7 +3255,7 @@ type GetMyStarredAssetsResponse struct { func (x *GetMyStarredAssetsResponse) Reset() { *x = GetMyStarredAssetsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[54] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3166,7 +3268,7 @@ func (x *GetMyStarredAssetsResponse) String() string { func (*GetMyStarredAssetsResponse) ProtoMessage() {} func (x *GetMyStarredAssetsResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[54] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3179,7 +3281,7 @@ func (x *GetMyStarredAssetsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyStarredAssetsResponse.ProtoReflect.Descriptor instead. func (*GetMyStarredAssetsResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{54} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{56} } func (x *GetMyStarredAssetsResponse) GetData() []*Asset { @@ -3200,7 +3302,7 @@ type GetMyStarredAssetRequest struct { func (x *GetMyStarredAssetRequest) Reset() { *x = GetMyStarredAssetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[55] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3213,7 +3315,7 @@ func (x *GetMyStarredAssetRequest) String() string { func (*GetMyStarredAssetRequest) ProtoMessage() {} func (x *GetMyStarredAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[55] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[57] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3226,7 +3328,7 @@ func (x *GetMyStarredAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyStarredAssetRequest.ProtoReflect.Descriptor instead. func (*GetMyStarredAssetRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{55} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{57} } func (x *GetMyStarredAssetRequest) GetAssetId() string { @@ -3247,7 +3349,7 @@ type GetMyStarredAssetResponse struct { func (x *GetMyStarredAssetResponse) Reset() { *x = GetMyStarredAssetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[56] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3260,7 +3362,7 @@ func (x *GetMyStarredAssetResponse) String() string { func (*GetMyStarredAssetResponse) ProtoMessage() {} func (x *GetMyStarredAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[56] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[58] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3273,7 +3375,7 @@ func (x *GetMyStarredAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyStarredAssetResponse.ProtoReflect.Descriptor instead. func (*GetMyStarredAssetResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{56} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{58} } func (x *GetMyStarredAssetResponse) GetData() *Asset { @@ -3294,7 +3396,7 @@ type StarAssetRequest struct { func (x *StarAssetRequest) Reset() { *x = StarAssetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[57] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3307,7 +3409,7 @@ func (x *StarAssetRequest) String() string { func (*StarAssetRequest) ProtoMessage() {} func (x *StarAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[57] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[59] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3320,7 +3422,7 @@ func (x *StarAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use StarAssetRequest.ProtoReflect.Descriptor instead. func (*StarAssetRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{57} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{59} } func (x *StarAssetRequest) GetAssetId() string { @@ -3341,7 +3443,7 @@ type StarAssetResponse struct { func (x *StarAssetResponse) Reset() { *x = StarAssetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[58] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3354,7 +3456,7 @@ func (x *StarAssetResponse) String() string { func (*StarAssetResponse) ProtoMessage() {} func (x *StarAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[58] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[60] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3367,7 +3469,7 @@ func (x *StarAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use StarAssetResponse.ProtoReflect.Descriptor instead. func (*StarAssetResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{58} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{60} } func (x *StarAssetResponse) GetId() string { @@ -3388,7 +3490,7 @@ type UnstarAssetRequest struct { func (x *UnstarAssetRequest) Reset() { *x = UnstarAssetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[59] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3401,7 +3503,7 @@ func (x *UnstarAssetRequest) String() string { func (*UnstarAssetRequest) ProtoMessage() {} func (x *UnstarAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[59] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3414,7 +3516,7 @@ func (x *UnstarAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UnstarAssetRequest.ProtoReflect.Descriptor instead. func (*UnstarAssetRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{59} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{61} } func (x *UnstarAssetRequest) GetAssetId() string { @@ -3433,7 +3535,7 @@ type UnstarAssetResponse struct { func (x *UnstarAssetResponse) Reset() { *x = UnstarAssetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[60] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3446,7 +3548,7 @@ func (x *UnstarAssetResponse) String() string { func (*UnstarAssetResponse) ProtoMessage() {} func (x *UnstarAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[60] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3459,7 +3561,7 @@ func (x *UnstarAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UnstarAssetResponse.ProtoReflect.Descriptor instead. func (*UnstarAssetResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{60} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{62} } type GetMyDiscussionsRequest struct { @@ -3481,7 +3583,7 @@ type GetMyDiscussionsRequest struct { func (x *GetMyDiscussionsRequest) Reset() { *x = GetMyDiscussionsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[61] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3494,7 +3596,7 @@ func (x *GetMyDiscussionsRequest) String() string { func (*GetMyDiscussionsRequest) ProtoMessage() {} func (x *GetMyDiscussionsRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[61] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3507,7 +3609,7 @@ func (x *GetMyDiscussionsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyDiscussionsRequest.ProtoReflect.Descriptor instead. func (*GetMyDiscussionsRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{61} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{63} } func (x *GetMyDiscussionsRequest) GetFilter() string { @@ -3584,7 +3686,7 @@ type GetMyDiscussionsResponse struct { func (x *GetMyDiscussionsResponse) Reset() { *x = GetMyDiscussionsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[62] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3597,7 +3699,7 @@ func (x *GetMyDiscussionsResponse) String() string { func (*GetMyDiscussionsResponse) ProtoMessage() {} func (x *GetMyDiscussionsResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[62] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3610,7 +3712,7 @@ func (x *GetMyDiscussionsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetMyDiscussionsResponse.ProtoReflect.Descriptor instead. func (*GetMyDiscussionsResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{62} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{64} } func (x *GetMyDiscussionsResponse) GetData() []*Discussion { @@ -3635,7 +3737,7 @@ type CreateTagAssetRequest struct { func (x *CreateTagAssetRequest) Reset() { *x = CreateTagAssetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[63] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3648,7 +3750,7 @@ func (x *CreateTagAssetRequest) String() string { func (*CreateTagAssetRequest) ProtoMessage() {} func (x *CreateTagAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[63] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3661,7 +3763,7 @@ func (x *CreateTagAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTagAssetRequest.ProtoReflect.Descriptor instead. func (*CreateTagAssetRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{63} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{65} } func (x *CreateTagAssetRequest) GetAssetId() string { @@ -3710,7 +3812,7 @@ type CreateTagAssetResponse struct { func (x *CreateTagAssetResponse) Reset() { *x = CreateTagAssetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[64] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3723,7 +3825,7 @@ func (x *CreateTagAssetResponse) String() string { func (*CreateTagAssetResponse) ProtoMessage() {} func (x *CreateTagAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[64] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3736,7 +3838,7 @@ func (x *CreateTagAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTagAssetResponse.ProtoReflect.Descriptor instead. func (*CreateTagAssetResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{64} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{66} } func (x *CreateTagAssetResponse) GetData() *Tag { @@ -3758,7 +3860,7 @@ type GetTagByAssetAndTemplateRequest struct { func (x *GetTagByAssetAndTemplateRequest) Reset() { *x = GetTagByAssetAndTemplateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[65] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3771,7 +3873,7 @@ func (x *GetTagByAssetAndTemplateRequest) String() string { func (*GetTagByAssetAndTemplateRequest) ProtoMessage() {} func (x *GetTagByAssetAndTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[65] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3784,7 +3886,7 @@ func (x *GetTagByAssetAndTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTagByAssetAndTemplateRequest.ProtoReflect.Descriptor instead. func (*GetTagByAssetAndTemplateRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{65} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{67} } func (x *GetTagByAssetAndTemplateRequest) GetAssetId() string { @@ -3812,7 +3914,7 @@ type GetTagByAssetAndTemplateResponse struct { func (x *GetTagByAssetAndTemplateResponse) Reset() { *x = GetTagByAssetAndTemplateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[66] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3825,7 +3927,7 @@ func (x *GetTagByAssetAndTemplateResponse) String() string { func (*GetTagByAssetAndTemplateResponse) ProtoMessage() {} func (x *GetTagByAssetAndTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[66] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[68] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3838,7 +3940,7 @@ func (x *GetTagByAssetAndTemplateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTagByAssetAndTemplateResponse.ProtoReflect.Descriptor instead. func (*GetTagByAssetAndTemplateResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{66} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{68} } func (x *GetTagByAssetAndTemplateResponse) GetData() *Tag { @@ -3863,7 +3965,7 @@ type UpdateTagAssetRequest struct { func (x *UpdateTagAssetRequest) Reset() { *x = UpdateTagAssetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[67] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3876,7 +3978,7 @@ func (x *UpdateTagAssetRequest) String() string { func (*UpdateTagAssetRequest) ProtoMessage() {} func (x *UpdateTagAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[67] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[69] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3889,7 +3991,7 @@ func (x *UpdateTagAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateTagAssetRequest.ProtoReflect.Descriptor instead. func (*UpdateTagAssetRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{67} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{69} } func (x *UpdateTagAssetRequest) GetAssetId() string { @@ -3938,7 +4040,7 @@ type UpdateTagAssetResponse struct { func (x *UpdateTagAssetResponse) Reset() { *x = UpdateTagAssetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[68] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3951,7 +4053,7 @@ func (x *UpdateTagAssetResponse) String() string { func (*UpdateTagAssetResponse) ProtoMessage() {} func (x *UpdateTagAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[68] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[70] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3964,7 +4066,7 @@ func (x *UpdateTagAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateTagAssetResponse.ProtoReflect.Descriptor instead. func (*UpdateTagAssetResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{68} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{70} } func (x *UpdateTagAssetResponse) GetData() *Tag { @@ -3986,7 +4088,7 @@ type DeleteTagAssetRequest struct { func (x *DeleteTagAssetRequest) Reset() { *x = DeleteTagAssetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[69] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3999,7 +4101,7 @@ func (x *DeleteTagAssetRequest) String() string { func (*DeleteTagAssetRequest) ProtoMessage() {} func (x *DeleteTagAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[69] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[71] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4012,7 +4114,7 @@ func (x *DeleteTagAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteTagAssetRequest.ProtoReflect.Descriptor instead. func (*DeleteTagAssetRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{69} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{71} } func (x *DeleteTagAssetRequest) GetAssetId() string { @@ -4038,7 +4140,7 @@ type DeleteTagAssetResponse struct { func (x *DeleteTagAssetResponse) Reset() { *x = DeleteTagAssetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[70] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4051,7 +4153,7 @@ func (x *DeleteTagAssetResponse) String() string { func (*DeleteTagAssetResponse) ProtoMessage() {} func (x *DeleteTagAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[70] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[72] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4064,7 +4166,7 @@ func (x *DeleteTagAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteTagAssetResponse.ProtoReflect.Descriptor instead. func (*DeleteTagAssetResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{70} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{72} } type GetAllTagsByAssetRequest struct { @@ -4078,7 +4180,7 @@ type GetAllTagsByAssetRequest struct { func (x *GetAllTagsByAssetRequest) Reset() { *x = GetAllTagsByAssetRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[71] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4091,7 +4193,7 @@ func (x *GetAllTagsByAssetRequest) String() string { func (*GetAllTagsByAssetRequest) ProtoMessage() {} func (x *GetAllTagsByAssetRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[71] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[73] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4104,7 +4206,7 @@ func (x *GetAllTagsByAssetRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAllTagsByAssetRequest.ProtoReflect.Descriptor instead. func (*GetAllTagsByAssetRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{71} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{73} } func (x *GetAllTagsByAssetRequest) GetAssetId() string { @@ -4125,7 +4227,7 @@ type GetAllTagsByAssetResponse struct { func (x *GetAllTagsByAssetResponse) Reset() { *x = GetAllTagsByAssetResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[72] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4138,7 +4240,7 @@ func (x *GetAllTagsByAssetResponse) String() string { func (*GetAllTagsByAssetResponse) ProtoMessage() {} func (x *GetAllTagsByAssetResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[72] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[74] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4151,7 +4253,7 @@ func (x *GetAllTagsByAssetResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAllTagsByAssetResponse.ProtoReflect.Descriptor instead. func (*GetAllTagsByAssetResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{72} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{74} } func (x *GetAllTagsByAssetResponse) GetData() []*Tag { @@ -4172,7 +4274,7 @@ type GetAllTagTemplatesRequest struct { func (x *GetAllTagTemplatesRequest) Reset() { *x = GetAllTagTemplatesRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[73] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4185,7 +4287,7 @@ func (x *GetAllTagTemplatesRequest) String() string { func (*GetAllTagTemplatesRequest) ProtoMessage() {} func (x *GetAllTagTemplatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[73] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[75] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4198,7 +4300,7 @@ func (x *GetAllTagTemplatesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAllTagTemplatesRequest.ProtoReflect.Descriptor instead. func (*GetAllTagTemplatesRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{73} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{75} } func (x *GetAllTagTemplatesRequest) GetUrn() string { @@ -4219,7 +4321,7 @@ type GetAllTagTemplatesResponse struct { func (x *GetAllTagTemplatesResponse) Reset() { *x = GetAllTagTemplatesResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[74] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4232,7 +4334,7 @@ func (x *GetAllTagTemplatesResponse) String() string { func (*GetAllTagTemplatesResponse) ProtoMessage() {} func (x *GetAllTagTemplatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[74] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[76] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4245,7 +4347,7 @@ func (x *GetAllTagTemplatesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAllTagTemplatesResponse.ProtoReflect.Descriptor instead. func (*GetAllTagTemplatesResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{74} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{76} } func (x *GetAllTagTemplatesResponse) GetData() []*TagTemplate { @@ -4269,7 +4371,7 @@ type CreateTagTemplateRequest struct { func (x *CreateTagTemplateRequest) Reset() { *x = CreateTagTemplateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[75] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4282,7 +4384,7 @@ func (x *CreateTagTemplateRequest) String() string { func (*CreateTagTemplateRequest) ProtoMessage() {} func (x *CreateTagTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[75] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[77] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4295,7 +4397,7 @@ func (x *CreateTagTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTagTemplateRequest.ProtoReflect.Descriptor instead. func (*CreateTagTemplateRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{75} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{77} } func (x *CreateTagTemplateRequest) GetUrn() string { @@ -4337,7 +4439,7 @@ type CreateTagTemplateResponse struct { func (x *CreateTagTemplateResponse) Reset() { *x = CreateTagTemplateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[76] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4350,7 +4452,7 @@ func (x *CreateTagTemplateResponse) String() string { func (*CreateTagTemplateResponse) ProtoMessage() {} func (x *CreateTagTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[76] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[78] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4363,7 +4465,7 @@ func (x *CreateTagTemplateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTagTemplateResponse.ProtoReflect.Descriptor instead. func (*CreateTagTemplateResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{76} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{78} } func (x *CreateTagTemplateResponse) GetData() *TagTemplate { @@ -4384,7 +4486,7 @@ type GetTagTemplateRequest struct { func (x *GetTagTemplateRequest) Reset() { *x = GetTagTemplateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[77] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4397,7 +4499,7 @@ func (x *GetTagTemplateRequest) String() string { func (*GetTagTemplateRequest) ProtoMessage() {} func (x *GetTagTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[77] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[79] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4410,7 +4512,7 @@ func (x *GetTagTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTagTemplateRequest.ProtoReflect.Descriptor instead. func (*GetTagTemplateRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{77} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{79} } func (x *GetTagTemplateRequest) GetTemplateUrn() string { @@ -4431,7 +4533,7 @@ type GetTagTemplateResponse struct { func (x *GetTagTemplateResponse) Reset() { *x = GetTagTemplateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[78] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4444,7 +4546,7 @@ func (x *GetTagTemplateResponse) String() string { func (*GetTagTemplateResponse) ProtoMessage() {} func (x *GetTagTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[78] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[80] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4457,7 +4559,7 @@ func (x *GetTagTemplateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTagTemplateResponse.ProtoReflect.Descriptor instead. func (*GetTagTemplateResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{78} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{80} } func (x *GetTagTemplateResponse) GetData() *TagTemplate { @@ -4481,7 +4583,7 @@ type UpdateTagTemplateRequest struct { func (x *UpdateTagTemplateRequest) Reset() { *x = UpdateTagTemplateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[79] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4494,7 +4596,7 @@ func (x *UpdateTagTemplateRequest) String() string { func (*UpdateTagTemplateRequest) ProtoMessage() {} func (x *UpdateTagTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[79] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[81] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4507,7 +4609,7 @@ func (x *UpdateTagTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateTagTemplateRequest.ProtoReflect.Descriptor instead. func (*UpdateTagTemplateRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{79} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{81} } func (x *UpdateTagTemplateRequest) GetTemplateUrn() string { @@ -4549,7 +4651,7 @@ type UpdateTagTemplateResponse struct { func (x *UpdateTagTemplateResponse) Reset() { *x = UpdateTagTemplateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[80] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4562,7 +4664,7 @@ func (x *UpdateTagTemplateResponse) String() string { func (*UpdateTagTemplateResponse) ProtoMessage() {} func (x *UpdateTagTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[80] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[82] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4575,7 +4677,7 @@ func (x *UpdateTagTemplateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateTagTemplateResponse.ProtoReflect.Descriptor instead. func (*UpdateTagTemplateResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{80} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{82} } func (x *UpdateTagTemplateResponse) GetData() *TagTemplate { @@ -4596,7 +4698,7 @@ type DeleteTagTemplateRequest struct { func (x *DeleteTagTemplateRequest) Reset() { *x = DeleteTagTemplateRequest{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[81] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4609,7 +4711,7 @@ func (x *DeleteTagTemplateRequest) String() string { func (*DeleteTagTemplateRequest) ProtoMessage() {} func (x *DeleteTagTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[81] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[83] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4622,7 +4724,7 @@ func (x *DeleteTagTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteTagTemplateRequest.ProtoReflect.Descriptor instead. func (*DeleteTagTemplateRequest) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{81} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{83} } func (x *DeleteTagTemplateRequest) GetTemplateUrn() string { @@ -4641,7 +4743,7 @@ type DeleteTagTemplateResponse struct { func (x *DeleteTagTemplateResponse) Reset() { *x = DeleteTagTemplateResponse{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[82] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4654,7 +4756,7 @@ func (x *DeleteTagTemplateResponse) String() string { func (*DeleteTagTemplateResponse) ProtoMessage() {} func (x *DeleteTagTemplateResponse) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[82] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[84] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4667,7 +4769,7 @@ func (x *DeleteTagTemplateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteTagTemplateResponse.ProtoReflect.Descriptor instead. func (*DeleteTagTemplateResponse) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{82} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{84} } type User struct { @@ -4686,7 +4788,7 @@ type User struct { func (x *User) Reset() { *x = User{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[83] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4699,7 +4801,7 @@ func (x *User) String() string { func (*User) ProtoMessage() {} func (x *User) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[83] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[85] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4712,7 +4814,7 @@ func (x *User) ProtoReflect() protoreflect.Message { // Deprecated: Use User.ProtoReflect.Descriptor instead. func (*User) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{83} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{85} } func (x *User) GetId() string { @@ -4771,7 +4873,7 @@ type Change struct { func (x *Change) Reset() { *x = Change{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[84] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4784,7 +4886,7 @@ func (x *Change) String() string { func (*Change) ProtoMessage() {} func (x *Change) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[84] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[86] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4797,7 +4899,7 @@ func (x *Change) ProtoReflect() protoreflect.Message { // Deprecated: Use Change.ProtoReflect.Descriptor instead. func (*Change) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{84} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{86} } func (x *Change) GetType() string { @@ -4854,7 +4956,7 @@ type Asset struct { func (x *Asset) Reset() { *x = Asset{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[85] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4867,7 +4969,7 @@ func (x *Asset) String() string { func (*Asset) ProtoMessage() {} func (x *Asset) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[85] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[87] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4880,7 +4982,7 @@ func (x *Asset) ProtoReflect() protoreflect.Message { // Deprecated: Use Asset.ProtoReflect.Descriptor instead. func (*Asset) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{85} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{87} } func (x *Asset) GetId() string { @@ -5012,7 +5114,7 @@ type Probe struct { func (x *Probe) Reset() { *x = Probe{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[86] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5025,7 +5127,7 @@ func (x *Probe) String() string { func (*Probe) ProtoMessage() {} func (x *Probe) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[86] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[88] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5038,7 +5140,7 @@ func (x *Probe) ProtoReflect() protoreflect.Message { // Deprecated: Use Probe.ProtoReflect.Descriptor instead. func (*Probe) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{86} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{88} } func (x *Probe) GetId() string { @@ -5111,7 +5213,7 @@ type Discussion struct { func (x *Discussion) Reset() { *x = Discussion{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[87] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5124,7 +5226,7 @@ func (x *Discussion) String() string { func (*Discussion) ProtoMessage() {} func (x *Discussion) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[87] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[89] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5137,7 +5239,7 @@ func (x *Discussion) ProtoReflect() protoreflect.Message { // Deprecated: Use Discussion.ProtoReflect.Descriptor instead. func (*Discussion) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{87} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{89} } func (x *Discussion) GetId() string { @@ -5234,7 +5336,7 @@ type Comment struct { func (x *Comment) Reset() { *x = Comment{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[88] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5247,7 +5349,7 @@ func (x *Comment) String() string { func (*Comment) ProtoMessage() {} func (x *Comment) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[88] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[90] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5260,7 +5362,7 @@ func (x *Comment) ProtoReflect() protoreflect.Message { // Deprecated: Use Comment.ProtoReflect.Descriptor instead. func (*Comment) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{88} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{90} } func (x *Comment) GetId() string { @@ -5325,7 +5427,7 @@ type LineageEdge struct { func (x *LineageEdge) Reset() { *x = LineageEdge{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[89] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5338,7 +5440,7 @@ func (x *LineageEdge) String() string { func (*LineageEdge) ProtoMessage() {} func (x *LineageEdge) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[89] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[91] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5351,7 +5453,7 @@ func (x *LineageEdge) ProtoReflect() protoreflect.Message { // Deprecated: Use LineageEdge.ProtoReflect.Descriptor instead. func (*LineageEdge) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{89} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{91} } func (x *LineageEdge) GetSource() string { @@ -5390,7 +5492,7 @@ type LineageNode struct { func (x *LineageNode) Reset() { *x = LineageNode{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[90] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5403,7 +5505,7 @@ func (x *LineageNode) String() string { func (*LineageNode) ProtoMessage() {} func (x *LineageNode) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[90] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[92] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5416,7 +5518,7 @@ func (x *LineageNode) ProtoReflect() protoreflect.Message { // Deprecated: Use LineageNode.ProtoReflect.Descriptor instead. func (*LineageNode) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{90} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{92} } func (x *LineageNode) GetUrn() string { @@ -5457,7 +5559,7 @@ type Tag struct { func (x *Tag) Reset() { *x = Tag{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[91] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5470,7 +5572,7 @@ func (x *Tag) String() string { func (*Tag) ProtoMessage() {} func (x *Tag) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[91] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[93] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5483,7 +5585,7 @@ func (x *Tag) ProtoReflect() protoreflect.Message { // Deprecated: Use Tag.ProtoReflect.Descriptor instead. func (*Tag) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{91} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{93} } func (x *Tag) GetAssetId() string { @@ -5541,7 +5643,7 @@ type TagValue struct { func (x *TagValue) Reset() { *x = TagValue{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[92] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5554,7 +5656,7 @@ func (x *TagValue) String() string { func (*TagValue) ProtoMessage() {} func (x *TagValue) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[92] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[94] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5567,7 +5669,7 @@ func (x *TagValue) ProtoReflect() protoreflect.Message { // Deprecated: Use TagValue.ProtoReflect.Descriptor instead. func (*TagValue) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{92} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{94} } func (x *TagValue) GetFieldId() uint32 { @@ -5656,7 +5758,7 @@ type TagTemplate struct { func (x *TagTemplate) Reset() { *x = TagTemplate{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[93] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5669,7 +5771,7 @@ func (x *TagTemplate) String() string { func (*TagTemplate) ProtoMessage() {} func (x *TagTemplate) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[93] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[95] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5682,7 +5784,7 @@ func (x *TagTemplate) ProtoReflect() protoreflect.Message { // Deprecated: Use TagTemplate.ProtoReflect.Descriptor instead. func (*TagTemplate) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{93} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{95} } func (x *TagTemplate) GetUrn() string { @@ -5746,7 +5848,7 @@ type TagTemplateField struct { func (x *TagTemplateField) Reset() { *x = TagTemplateField{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[94] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5759,7 +5861,7 @@ func (x *TagTemplateField) String() string { func (*TagTemplateField) ProtoMessage() {} func (x *TagTemplateField) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[94] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[96] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5772,7 +5874,7 @@ func (x *TagTemplateField) ProtoReflect() protoreflect.Message { // Deprecated: Use TagTemplateField.ProtoReflect.Descriptor instead. func (*TagTemplateField) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{94} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{96} } func (x *TagTemplateField) GetId() uint32 { @@ -5850,7 +5952,7 @@ type Type struct { func (x *Type) Reset() { *x = Type{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[95] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5863,7 +5965,7 @@ func (x *Type) String() string { func (*Type) ProtoMessage() {} func (x *Type) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[95] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[97] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5876,7 +5978,7 @@ func (x *Type) ProtoReflect() protoreflect.Message { // Deprecated: Use Type.ProtoReflect.Descriptor instead. func (*Type) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{95} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{97} } func (x *Type) GetName() string { @@ -5904,7 +6006,7 @@ type GetGraphResponse_ProbesInfo struct { func (x *GetGraphResponse_ProbesInfo) Reset() { *x = GetGraphResponse_ProbesInfo{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[99] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5917,7 +6019,7 @@ func (x *GetGraphResponse_ProbesInfo) String() string { func (*GetGraphResponse_ProbesInfo) ProtoMessage() {} func (x *GetGraphResponse_ProbesInfo) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[99] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[101] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5951,7 +6053,7 @@ type GetGraphResponse_NodeAttributes struct { func (x *GetGraphResponse_NodeAttributes) Reset() { *x = GetGraphResponse_NodeAttributes{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[100] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5964,7 +6066,7 @@ func (x *GetGraphResponse_NodeAttributes) String() string { func (*GetGraphResponse_NodeAttributes) ProtoMessage() {} func (x *GetGraphResponse_NodeAttributes) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[100] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[102] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6006,7 +6108,7 @@ type UpsertAssetRequest_Asset struct { func (x *UpsertAssetRequest_Asset) Reset() { *x = UpsertAssetRequest_Asset{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[104] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6019,7 +6121,7 @@ func (x *UpsertAssetRequest_Asset) String() string { func (*UpsertAssetRequest_Asset) ProtoMessage() {} func (x *UpsertAssetRequest_Asset) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[104] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[106] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6117,7 +6219,7 @@ type UpsertPatchAssetRequest_Asset struct { func (x *UpsertPatchAssetRequest_Asset) Reset() { *x = UpsertPatchAssetRequest_Asset{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[106] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6130,7 +6232,7 @@ func (x *UpsertPatchAssetRequest_Asset) String() string { func (*UpsertPatchAssetRequest_Asset) ProtoMessage() {} func (x *UpsertPatchAssetRequest_Asset) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[106] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[108] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6224,7 +6326,7 @@ type CreateAssetProbeRequest_Probe struct { func (x *CreateAssetProbeRequest_Probe) Reset() { *x = CreateAssetProbeRequest_Probe{} if protoimpl.UnsafeEnabled { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[108] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6237,7 +6339,7 @@ func (x *CreateAssetProbeRequest_Probe) String() string { func (*CreateAssetProbeRequest_Probe) ProtoMessage() {} func (x *CreateAssetProbeRequest_Probe) ProtoReflect() protoreflect.Message { - mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[108] + mi := &file_gotocompany_compass_v1beta1_service_proto_msgTypes[110] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6250,7 +6352,7 @@ func (x *CreateAssetProbeRequest_Probe) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateAssetProbeRequest_Probe.ProtoReflect.Descriptor instead. func (*CreateAssetProbeRequest_Probe) Descriptor() ([]byte, []int) { - return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{47, 0} + return file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP(), []int{49, 0} } func (x *CreateAssetProbeRequest_Probe) GetId() string { @@ -6868,273 +6970,260 @@ var file_gotocompany_compass_v1beta1_service_proto_rawDesc = []byte{ 0x24, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6d, 0x0a, 0x19, - 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x67, 0x61, 0x7a, 0x65, - 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x04, 0x73, 0x69, 0x7a, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, 0x00, - 0x40, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, - 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, - 0x00, 0x40, 0x01, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x53, 0x0a, 0x1a, 0x47, - 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x67, 0x61, 0x7a, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x22, 0x71, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x1d, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, - 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, 0x00, 0x40, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, - 0x12, 0x21, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, 0x00, 0x40, 0x01, 0x52, 0x06, 0x6f, 0x66, 0x66, - 0x73, 0x65, 0x74, 0x22, 0x58, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, - 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x44, 0x0a, - 0x18, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x22, 0x53, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, - 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, - 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xec, 0x02, 0x0a, 0x17, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x75, 0x72, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x55, 0x72, 0x6e, 0x12, 0x50, 0x0a, 0x05, 0x70, 0x72, - 0x6f, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, - 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x1a, 0xd8, 0x01, 0x0a, - 0x05, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x13, 0x92, 0x41, 0x09, 0xd2, 0x01, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, 0x65, - 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, - 0x75, 0x63, 0x74, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x38, 0x0a, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 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, 0x52, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x2a, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x51, 0x0a, 0x11, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x20, 0x92, 0x41, 0x1d, 0x32, - 0x1b, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x62, 0x79, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, - 0x70, 0x6c, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x08, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x78, 0x0a, 0x1b, - 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, - 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xab, 0x02, 0x0a, + 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x6f, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x65, 0x78, + 0x70, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x50, 0x92, 0x41, 0x46, 0x32, 0x44, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x62, 0x79, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x65, + 0x78, 0x70, 0x72, 0x2c, 0x20, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x3a, 0x20, 0x72, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x20, 0x3c, 0x3d, 0x20, 0x22, 0x32, + 0x30, 0x32, 0x34, 0x2d, 0x30, 0x38, 0x2d, 0x30, 0x35, 0x54, 0x32, 0x33, 0x3a, 0x35, 0x39, 0x3a, + 0x35, 0x39, 0x22, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x03, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x45, 0x78, 0x70, 0x72, 0x12, 0xa2, 0x01, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x42, 0x88, 0x01, 0x92, 0x41, 0x84, 0x01, 0x32, 0x81, + 0x01, 0x69, 0x66, 0x20, 0x73, 0x65, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, 0x75, 0x65, 0x2c, + 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x73, + 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x65, + 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x72, + 0x6f, 0x77, 0x73, 0x2e, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x2c, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, + 0x70, 0x65, 0x72, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, + 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x62, 0x61, 0x63, 0x6b, 0x67, 0x72, 0x6f, 0x75, + 0x6e, 0x64, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x22, 0x3b, 0x0a, 0x14, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, + 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x61, 0x66, 0x66, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x52, 0x6f, 0x77, 0x73, 0x22, 0x6d, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x67, 0x61, 0x7a, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, 0x00, 0x40, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, 0x00, 0x40, 0x01, 0x52, 0x06, - 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x56, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, - 0x72, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, - 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x73, - 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, - 0x28, 0x00, 0x40, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, - 0x04, 0x28, 0x00, 0x40, 0x01, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x54, 0x0a, - 0x1a, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x53, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x67, 0x61, 0x7a, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x71, 0x0a, 0x1d, 0x47, + 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x69, + 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, + 0x04, 0x28, 0x00, 0x40, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x06, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, + 0x2a, 0x04, 0x28, 0x00, 0x40, 0x01, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x58, + 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x44, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x53, + 0x0a, 0x19, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x22, 0x35, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, - 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x22, 0x53, 0x0a, 0x19, 0x47, 0x65, - 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, - 0x2d, 0x0a, 0x10, 0x53, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x22, 0x23, - 0x0a, 0x11, 0x53, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x2f, 0x0a, 0x12, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x49, 0x64, 0x22, 0x15, 0x0a, 0x13, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc3, 0x02, 0x0a, 0x17, - 0x47, 0x65, 0x74, 0x4d, 0x79, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, - 0x01, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, - 0x01, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, 0x01, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, 0x01, - 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, - 0x01, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x0a, 0x04, 0x73, 0x6f, 0x72, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, - 0x01, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, - 0x03, 0xd0, 0x01, 0x01, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1d, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, + 0x61, 0x74, 0x61, 0x22, 0xec, 0x02, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x24, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x55, 0x72, 0x6e, 0x12, 0x50, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x72, + 0x6f, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, + 0x52, 0x05, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x1a, 0xd8, 0x01, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x62, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x13, 0x92, 0x41, 0x09, 0xd2, 0x01, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0xfa, + 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, + 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x61, + 0x73, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 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, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x22, 0x2a, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x51, + 0x0a, 0x11, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x09, 0x42, 0x20, 0x92, 0x41, 0x1d, 0x32, 0x1b, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x20, 0x62, 0x79, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x73, 0x22, 0x14, 0x0a, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x78, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x55, 0x73, + 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x1d, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, 0x00, 0x40, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x21, - 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, + 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, 0x00, 0x40, 0x01, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x22, 0x57, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x67, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, - 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xeb, 0x02, 0x0a, 0x15, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x74, 0x22, 0x56, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, + 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5d, 0x0a, 0x19, 0x47, 0x65, 0x74, + 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, 0x00, 0x40, 0x01, 0x52, + 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, 0x00, 0x40, 0x01, + 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x54, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x4d, + 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x35, + 0x0a, 0x18, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x49, 0x64, 0x22, 0x53, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, + 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x36, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x2d, 0x0a, 0x10, 0x53, 0x74, + 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, + 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x22, 0x23, 0x0a, 0x11, 0x53, 0x74, 0x61, + 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2f, + 0x0a, 0x12, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, - 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, - 0x72, 0x6e, 0x12, 0x44, 0x0a, 0x0a, 0x74, 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x22, + 0x15, 0x0a, 0x13, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc3, 0x02, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x4d, 0x79, + 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x20, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, 0x01, 0x52, 0x06, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, 0x01, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, 0x01, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, 0x01, 0x52, 0x05, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x12, 0x20, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, 0x01, 0x52, 0x06, 0x6c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1c, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, 0x01, 0x52, 0x04, 0x73, 0x6f, + 0x72, 0x74, 0x12, 0x26, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x72, 0x03, 0xd0, 0x01, 0x01, 0x52, + 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x04, 0x73, 0x69, + 0x7a, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, 0x28, + 0x00, 0x40, 0x01, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x06, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x09, 0xfa, 0x42, 0x06, 0x2a, 0x04, + 0x28, 0x00, 0x40, 0x01, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x57, 0x0a, 0x18, + 0x47, 0x65, 0x74, 0x4d, 0x79, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x74, - 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3a, - 0x67, 0x92, 0x41, 0x64, 0x0a, 0x62, 0x2a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, - 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, 0x22, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x6e, - 0x74, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, - 0x67, 0xd2, 0x01, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x0c, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0xd2, 0x01, 0x0a, 0x74, 0x61, - 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x4e, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x34, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, - 0x61, 0x67, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x5f, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x54, - 0x61, 0x67, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6e, 0x64, 0x54, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, 0x22, 0x58, 0x0a, 0x20, 0x47, 0x65, 0x74, - 0x54, 0x61, 0x67, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6e, 0x64, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xeb, 0x02, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, 0x12, 0x44, 0x0a, + 0x0a, 0x74, 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x74, 0x61, 0x67, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, + 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x67, 0x92, 0x41, 0x64, 0x0a, + 0x62, 0x2a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, 0x22, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0xd2, 0x01, 0x08, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0xd2, 0x01, 0x0a, 0x74, 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x22, 0x4e, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x22, 0xe3, 0x02, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, - 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, - 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, 0x12, 0x44, 0x0a, 0x0a, 0x74, - 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, - 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x74, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, - 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, - 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x5f, 0x92, 0x41, 0x5c, 0x0a, 0x5a, 0x2a, - 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, 0x29, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, - 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x74, 0x61, - 0x67, 0xd2, 0x01, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x0a, 0x74, - 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x4e, 0x0a, 0x16, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x54, 0x61, 0x67, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x55, 0x0a, 0x15, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, - 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, - 0x22, 0x18, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x18, 0x47, 0x65, - 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, + 0x61, 0x74, 0x61, 0x22, 0x5f, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x42, 0x79, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x41, 0x6e, 0x64, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, - 0x64, 0x22, 0x51, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x42, - 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, + 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x55, 0x72, 0x6e, 0x22, 0x58, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x42, 0x79, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6e, 0x64, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xe3, + 0x02, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, + 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, 0x12, 0x44, 0x0a, 0x0a, 0x74, 0x61, 0x67, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x52, 0x09, 0x74, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x31, 0x0a, 0x14, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x3a, 0x5f, 0x92, 0x41, 0x5c, 0x0a, 0x5a, 0x2a, 0x15, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x32, 0x29, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, + 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, + 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x74, 0x61, 0x67, 0xd2, 0x01, 0x08, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0xd2, 0x01, 0x0a, 0x74, 0x61, 0x67, 0x5f, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x22, 0x4e, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, + 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x22, 0x2d, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, - 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x75, 0x72, 0x6e, 0x22, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, - 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x28, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, - 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, - 0xb8, 0x02, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, - 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x21, - 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x04, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, - 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x69, 0x65, - 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x3a, 0x7e, 0x92, 0x41, 0x7b, 0x0a, - 0x79, 0x2a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, 0x2d, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, - 0x74, 0x6f, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x27, - 0x73, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0xd2, 0x01, 0x03, 0x75, 0x72, 0x6e, - 0xd2, 0x01, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0xd2, - 0x01, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0xd2, 0x01, 0x0a, - 0x74, 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x59, 0x0a, 0x19, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x3a, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x54, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, - 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, - 0x6e, 0x22, 0x56, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xd0, 0x02, 0x0a, 0x18, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, + 0x64, 0x61, 0x74, 0x61, 0x22, 0x55, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, + 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, + 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, 0x22, 0x18, 0x0a, 0x16, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, + 0x61, 0x67, 0x73, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x22, 0x51, 0x0a, 0x19, + 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, + 0x2d, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, + 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x22, 0x5a, + 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xb8, 0x02, 0x0a, 0x18, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, @@ -7143,1005 +7232,1056 @@ var file_gotocompany_compass_v1beta1_service_proto_rawDesc = []byte{ 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x3a, 0x84, 0x01, 0x92, 0x41, 0x80, 0x01, 0x0a, 0x7e, 0x2a, 0x18, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, 0x2d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x27, 0x73, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0xd2, 0x01, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0xd2, 0x01, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0xd2, 0x01, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0xd2, 0x01, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x59, 0x0a, 0x19, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x3d, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, - 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, 0x22, 0x1b, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0xdf, 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x75, 0x75, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, - 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, - 0x18, 0x05, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, - 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 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, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x0b, 0x92, 0x41, 0x08, 0x0a, 0x06, 0x2a, - 0x04, 0x55, 0x73, 0x65, 0x72, 0x22, 0x93, 0x01, 0x0a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2a, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, - 0x18, 0x03, 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, - 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x26, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x04, 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, 0x02, 0x74, 0x6f, 0x3a, 0x0d, 0x92, 0x41, - 0x0a, 0x0a, 0x08, 0x2a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xe9, 0x05, 0x0a, 0x05, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x46, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, - 0x6c, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x2e, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, - 0x12, 0x39, 0x0a, 0x06, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, - 0x73, 0x65, 0x72, 0x52, 0x06, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x40, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x62, 0x79, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x69, 0x65, 0x6c, 0x64, 0x73, 0x3a, 0x7e, 0x92, 0x41, 0x7b, 0x0a, 0x79, 0x2a, 0x18, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, 0x2d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, + 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x27, 0x73, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0xd2, 0x01, 0x03, 0x75, 0x72, 0x6e, 0xd2, 0x01, 0x0c, 0x64, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0xd2, 0x01, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0xd2, 0x01, 0x0a, 0x74, 0x61, 0x67, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x22, 0x59, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, + 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, + 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x22, 0x3a, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, 0x22, 0x56, 0x0a, 0x16, + 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x22, 0xd0, 0x02, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, + 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x55, 0x72, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x06, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x09, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x41, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x6c, 0x6f, 0x67, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, - 0x09, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 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, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x61, 0x74, 0x18, 0x0e, 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, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, - 0x72, 0x6c, 0x12, 0x3a, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, + 0x3a, 0x84, 0x01, 0x92, 0x41, 0x80, 0x01, 0x0a, 0x7e, 0x2a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x32, 0x2d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x74, 0x6f, 0x20, + 0x62, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x27, 0x73, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0xd2, 0x01, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, + 0x6e, 0xd2, 0x01, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0xd2, 0x01, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0xd2, 0x01, + 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x59, 0x0a, 0x19, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x73, 0x1a, 0x39, - 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x0c, 0x92, 0x41, 0x09, 0x0a, 0x07, - 0x2a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x22, 0xa9, 0x02, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x62, - 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x55, 0x72, 0x6e, 0x12, 0x16, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 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, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x07, 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, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x0c, 0x92, 0x41, 0x09, 0x0a, 0x07, 0x2a, 0x05, 0x50, 0x72, - 0x6f, 0x62, 0x65, 0x22, 0x80, 0x03, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, - 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x16, - 0x0a, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x69, 0x67, - 0x6e, 0x65, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, - 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x39, 0x0a, - 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x2e, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x22, 0x3d, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, + 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, + 0x6e, 0x22, 0x1b, 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdf, + 0x01, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x75, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x75, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, + 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, + 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x39, 0x0a, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 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, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x64, 0x41, 0x74, 0x3a, 0x11, 0x92, 0x41, 0x0e, 0x0a, 0x0c, 0x2a, 0x0a, 0x44, 0x69, 0x73, 0x63, - 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd3, 0x02, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, - 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x63, 0x75, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x37, 0x0a, 0x05, 0x6f, - 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x6f, - 0x77, 0x6e, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, - 0x62, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x09, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, - 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, - 0x07, 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, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x0e, 0x92, 0x41, - 0x0b, 0x0a, 0x09, 0x2a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x7e, 0x0a, 0x0b, - 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x67, 0x65, 0x45, 0x64, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, - 0x72, 0x6f, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x52, 0x04, 0x70, 0x72, 0x6f, 0x70, 0x3a, 0x12, 0x92, 0x41, 0x0f, 0x0a, 0x0d, 0x2a, - 0x0b, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x67, 0x65, 0x45, 0x64, 0x67, 0x65, 0x22, 0x69, 0x0a, 0x0b, - 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x67, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, - 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x16, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x3a, 0x12, 0x92, 0x41, 0x0f, 0x0a, 0x0d, 0x2a, 0x0b, 0x4c, 0x69, 0x6e, 0x65, - 0x61, 0x67, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x22, 0xfc, 0x01, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, - 0x19, 0x0a, 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x61, 0x73, 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, 0x12, 0x44, 0x0a, - 0x0a, 0x74, 0x61, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x25, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x09, 0x74, 0x61, 0x67, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, - 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x70, - 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x0a, 0x92, 0x41, 0x07, 0x0a, - 0x05, 0x2a, 0x03, 0x54, 0x61, 0x67, 0x22, 0xd1, 0x03, 0x0a, 0x08, 0x54, 0x61, 0x67, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x37, - 0x0a, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 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, 0x0a, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x55, 0x72, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x69, - 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x10, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x26, 0x0a, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, - 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0e, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x18, 0x09, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, - 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 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, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x0f, 0x92, 0x41, 0x0c, 0x0a, 0x0a, - 0x2a, 0x08, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xb5, 0x02, 0x0a, 0x0b, 0x54, - 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x21, 0x0a, 0x0c, - 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x45, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x64, 0x41, 0x74, 0x3a, 0x0b, 0x92, 0x41, 0x08, 0x0a, 0x06, 0x2a, 0x04, 0x55, 0x73, 0x65, 0x72, + 0x22, 0x93, 0x01, 0x0a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x12, 0x2a, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x03, 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, 0x66, 0x72, 0x6f, 0x6d, 0x12, + 0x26, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x04, 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, 0x02, 0x74, 0x6f, 0x3a, 0x0d, 0x92, 0x41, 0x0a, 0x0a, 0x08, 0x2a, 0x06, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x22, 0xe9, 0x05, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x72, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x46, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x08, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x39, 0x0a, 0x06, 0x6f, + 0x77, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x06, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x40, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x0b, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x42, 0x79, 0x12, 0x41, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x18, + 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x6c, 0x6f, 0x67, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0e, + 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, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x72, 0x6c, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x3a, 0x0a, + 0x06, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x62, + 0x65, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, + 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x0c, 0x92, 0x41, 0x09, 0x0a, 0x07, 0x2a, 0x05, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x22, 0xa9, 0x02, 0x0a, 0x05, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x61, 0x73, 0x73, 0x65, 0x74, 0x55, 0x72, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x73, + 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x38, 0x0a, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 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, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x5f, 0x61, 0x74, 0x18, 0x07, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, + 0x3a, 0x0c, 0x92, 0x41, 0x09, 0x0a, 0x07, 0x2a, 0x05, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x22, 0x80, + 0x03, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, + 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x65, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x65, 0x73, 0x12, + 0x37, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, - 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x74, 0x18, 0x0b, 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, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x12, - 0x92, 0x41, 0x0f, 0x0a, 0x0d, 0x2a, 0x0b, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x22, 0xdb, 0x02, 0x0a, 0x10, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, - 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, - 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, - 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x11, + 0x92, 0x41, 0x0e, 0x0a, 0x0c, 0x2a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x22, 0xd3, 0x02, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, + 0x0d, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x37, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, + 0x40, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x42, + 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, + 0x06, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x07, 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, 0x75, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x17, 0x92, 0x41, 0x14, 0x0a, 0x12, 0x2a, 0x10, - 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x22, 0x30, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 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, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x32, 0xef, 0x56, 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0xc4, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, - 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x35, 0x2e, 0x67, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, - 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, - 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x0e, 0x92, 0x41, 0x0b, 0x0a, 0x09, 0x2a, 0x07, + 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x7e, 0x0a, 0x0b, 0x4c, 0x69, 0x6e, 0x65, 0x61, + 0x67, 0x65, 0x45, 0x64, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x70, 0x72, 0x6f, 0x70, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x70, + 0x72, 0x6f, 0x70, 0x3a, 0x12, 0x92, 0x41, 0x0f, 0x0a, 0x0d, 0x2a, 0x0b, 0x4c, 0x69, 0x6e, 0x65, + 0x61, 0x67, 0x65, 0x45, 0x64, 0x67, 0x65, 0x22, 0x69, 0x0a, 0x0b, 0x4c, 0x69, 0x6e, 0x65, 0x61, + 0x67, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x16, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x1c, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x3a, 0x12, + 0x92, 0x41, 0x0f, 0x0a, 0x0d, 0x2a, 0x0b, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x67, 0x65, 0x4e, 0x6f, + 0x64, 0x65, 0x22, 0xfc, 0x01, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x55, 0x72, 0x6e, 0x12, 0x44, 0x0a, 0x0a, 0x74, 0x61, 0x67, 0x5f, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x09, 0x74, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x32, + 0x0a, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x13, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x0a, 0x92, 0x41, 0x07, 0x0a, 0x05, 0x2a, 0x03, 0x54, 0x61, + 0x67, 0x22, 0xd1, 0x03, 0x0a, 0x08, 0x54, 0x61, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x0b, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 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, 0x0a, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x75, 0x72, 0x6e, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x55, 0x72, 0x6e, 0x12, + 0x2c, 0x0a, 0x12, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, + 0x11, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x61, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x39, + 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x09, 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, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 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, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x3a, 0x0f, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x2a, 0x08, 0x54, 0x61, 0x67, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xb5, 0x02, 0x0a, 0x0b, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, 0x0a, 0x06, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x06, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x18, 0x05, 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, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, + 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 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, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x12, 0x92, 0x41, 0x0f, 0x0a, 0x0d, + 0x2a, 0x0b, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x22, 0xdb, 0x02, + 0x0a, 0x10, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x75, 0x72, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, + 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x61, + 0x74, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x0a, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 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, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x09, 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, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x41, 0x74, 0x3a, 0x17, 0x92, 0x41, 0x14, 0x0a, 0x12, 0x2a, 0x10, 0x54, 0x61, 0x67, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x30, 0x0a, 0x04, 0x54, + 0x79, 0x70, 0x65, 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, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xe2, 0x58, + 0x0a, 0x0e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x12, 0xc4, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x44, 0x69, 0x73, 0x63, 0x75, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x44, 0x69, 0x73, 0x63, 0x75, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x6c, 0x6c, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x92, 0x41, 0x21, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, + 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, + 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, + 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xc4, 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x40, 0x92, 0x41, 0x21, 0x0a, - 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x47, 0x65, 0x74, - 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xc4, 0x01, 0x0a, - 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, - 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, - 0x92, 0x41, 0x21, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0xba, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x75, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x92, 0x41, - 0x1e, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x47, - 0x65, 0x74, 0x20, 0x61, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, - 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, - 0x12, 0xc5, 0x01, 0x0a, 0x0f, 0x50, 0x61, 0x74, 0x63, 0x68, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x43, 0x92, 0x41, 0x21, 0x0a, 0x0a, + 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x20, 0x61, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xba, + 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0x92, 0x41, 0x1e, 0x0a, 0x0a, 0x44, 0x69, + 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x47, 0x65, 0x74, 0x20, 0x61, 0x20, + 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, + 0x12, 0x19, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xc5, 0x01, 0x0a, 0x0f, + 0x50, 0x61, 0x74, 0x63, 0x68, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, + 0x74, 0x63, 0x68, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x74, 0x63, 0x68, 0x44, 0x69, 0x73, - 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x47, 0x92, 0x41, 0x20, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x12, 0x50, 0x61, 0x74, 0x63, 0x68, 0x20, 0x61, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x32, 0x19, 0x2f, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xea, 0x01, 0x0a, 0x0d, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, - 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, - 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x72, 0x92, 0x41, 0x37, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, - 0x20, 0x61, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x32, 0x3a, 0x01, 0x2a, 0x22, 0x2d, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x64, 0x69, - 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0xea, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, - 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x47, 0x92, 0x41, 0x20, 0x0a, + 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x50, 0x61, 0x74, + 0x63, 0x68, 0x20, 0x61, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x32, 0x19, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, + 0x69, 0x64, 0x7d, 0x12, 0xea, 0x01, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6d, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x67, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, - 0x6c, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x6f, 0x92, 0x41, 0x37, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x47, 0x65, 0x74, - 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, - 0x20, 0x61, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, - 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x64, 0x69, 0x73, 0x63, 0x75, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x12, 0xe0, 0x01, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, - 0x74, 0x12, 0x2e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x71, 0x92, 0x41, 0x34, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x47, 0x65, - 0x74, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, - 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x34, 0x12, 0x32, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, + 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x72, 0x92, 0x41, + 0x37, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x0a, 0x07, 0x43, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, + 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x64, 0x69, + 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x3a, 0x01, + 0x2a, 0x22, 0x2d, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xef, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, - 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, - 0x92, 0x41, 0x37, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x0a, - 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, - 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x37, - 0x3a, 0x01, 0x2a, 0x1a, 0x32, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, - 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x64, 0x69, 0x73, 0x63, 0x75, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xec, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, - 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x74, 0x92, 0x41, 0x37, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, - 0x61, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x34, 0x2a, 0x32, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, - 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, - 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xc2, 0x06, 0x0a, 0x0c, 0x53, 0x65, 0x61, 0x72, 0x63, - 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x12, 0xea, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6d, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xcc, 0x05, 0x92, - 0x41, 0xb1, 0x05, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x0a, 0x05, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x12, 0x13, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, - 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x8a, 0x05, 0x41, 0x50, 0x49, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, - 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x20, 0x27, 0x74, 0x65, 0x78, 0x74, 0x27, 0x20, 0x69, 0x73, 0x20, - 0x66, 0x75, 0x7a, 0x7a, 0x79, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x20, 0x61, 0x67, - 0x61, 0x69, 0x6e, 0x73, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x73, - 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x20, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x73, 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, - 0x65, 0x64, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x73, 0x70, 0x65, 0x63, - 0x69, 0x66, 0x79, 0x20, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x6d, - 0x61, 0x74, 0x63, 0x68, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x20, 0x75, 0x73, - 0x69, 0x6e, 0x67, 0x20, 0x27, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x2e, 0x2a, 0x5d, 0x27, - 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x73, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, - 0x66, 0x79, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x6d, - 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x74, 0x6f, - 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x61, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, - 0x66, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x6f, - 0x73, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x20, 0x46, 0x6f, 0x72, 0x20, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2c, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, - 0x63, 0x69, 0x66, 0x79, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x63, 0x61, - 0x70, 0x65, 0x20, 0x27, 0x76, 0x6e, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x27, 0x74, 0x68, 0x27, - 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x63, 0x6f, 0x75, 0x6c, - 0x64, 0x20, 0x62, 0x65, 0x20, 0x60, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x3f, 0x74, - 0x65, 0x78, 0x74, 0x3d, 0x3c, 0x74, 0x65, 0x78, 0x74, 0x3e, 0x26, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x5b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5d, 0x3d, 0x69, - 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x26, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x5b, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x63, 0x61, 0x70, 0x65, 0x5d, 0x3d, 0x76, 0x6e, 0x2c, - 0x74, 0x68, 0x60, 0x2e, 0x20, 0x41, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x41, 0x50, 0x49, - 0x20, 0x61, 0x6c, 0x73, 0x6f, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, 0x66, - 0x75, 0x7a, 0x7a, 0x79, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x6d, 0x61, 0x74, 0x63, - 0x68, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x27, 0x71, 0x75, 0x65, 0x72, 0x79, 0x27, 0x20, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x20, 0x46, 0x6f, 0x72, - 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2c, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, - 0x68, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, - 0x20, 0x68, 0x61, 0x73, 0x20, 0x27, 0x62, 0x69, 0x67, 0x71, 0x75, 0x27, 0x20, 0x74, 0x65, 0x72, - 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x69, 0x74, 0x73, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x60, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x3f, 0x74, - 0x65, 0x78, 0x74, 0x3d, 0x3c, 0x74, 0x65, 0x78, 0x74, 0x3e, 0x26, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x5b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5d, 0x3d, 0x62, 0x69, - 0x67, 0x71, 0x75, 0x60, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0xa9, 0x08, 0x0a, 0x0b, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x2f, 0x2e, 0x67, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, - 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x43, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6f, 0x92, 0x41, + 0x37, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x0a, 0x07, 0x43, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x64, 0x69, + 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12, 0x2d, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0xe0, 0x01, + 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb6, - 0x07, 0x92, 0x41, 0x96, 0x07, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x0a, 0x05, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x12, 0x14, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x61, 0x70, 0x69, 0x20, 0x66, - 0x6f, 0x72, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x1a, 0xef, 0x06, 0x41, 0x50, 0x49, 0x20, - 0x66, 0x6f, 0x72, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x6f, 0x63, - 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x20, 0x27, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x62, 0x79, - 0x27, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x64, 0x20, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, - 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x20, - 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, - 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x20, - 0x61, 0x73, 0x20, 0x77, 0x65, 0x6c, 0x6c, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x69, 0x74, 0x20, 0x69, - 0x73, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x73, 0x2c, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x74, 0x6f, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, - 0x6e, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x20, 0x75, 0x73, - 0x69, 0x6e, 0x67, 0x20, 0x27, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x2e, 0x2a, 0x5d, 0x27, - 0x20, 0x74, 0x6f, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x74, - 0x68, 0x6f, 0x73, 0x65, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x20, - 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, - 0x65, 0x61, 0x63, 0x68, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x6d, 0x75, 0x6c, 0x74, - 0x69, 0x70, 0x6c, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, - 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x61, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65, 0x20, - 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x2c, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, - 0x79, 0x20, 0x74, 0x77, 0x6f, 0x20, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, - 0x27, 0x76, 0x6e, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x27, 0x74, 0x68, 0x27, 0x2c, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, - 0x65, 0x20, 0x60, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, - 0x3f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x62, 0x79, 0x3d, 0x3c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x62, - 0x79, 0x3e, 0x26, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, - 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5d, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x26, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x63, - 0x61, 0x70, 0x65, 0x5d, 0x3d, 0x76, 0x6e, 0x2c, 0x74, 0x68, 0x60, 0x2e, 0x20, 0x27, 0x69, 0x6e, - 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x27, 0x20, 0x69, 0x73, - 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6f, 0x6e, - 0x6c, 0x79, 0x20, 0x74, 0x68, 0x65, 0x73, 0x65, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x20, - 0x69, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x20, 0x74, 0x68, 0x69, - 0x73, 0x20, 0x68, 0x65, 0x6c, 0x70, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, - 0x69, 0x63, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2e, 0x20, 0x45, 0x67, 0x3a, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x2f, 0x3f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x62, 0x79, 0x3d, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x31, 0x26, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x62, 0x79, 0x3d, 0x66, 0x69, 0x65, - 0x6c, 0x64, 0x32, 0x26, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x65, 0x6e, 0x76, 0x69, 0x72, - 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5d, 0x3d, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x26, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x73, 0x3d, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, - 0x73, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x26, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x3d, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x61, 0x74, - 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x20, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x20, 0x62, 0x79, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, - 0x62, 0x65, 0x20, 0x70, 0x61, 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x2c, 0x20, 0x6e, 0x6f, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, - 0x61, 0x64, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x69, 0x6e, 0x63, 0x6c, - 0x75, 0x64, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0xbc, 0x02, 0x0a, 0x0d, 0x53, 0x75, 0x67, 0x67, - 0x65, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, - 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0xc3, 0x01, 0x92, 0x41, 0xa0, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x0a, - 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x10, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, - 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x7d, 0x41, 0x50, 0x49, 0x20, 0x66, 0x6f, - 0x72, 0x20, 0x72, 0x65, 0x74, 0x72, 0x65, 0x69, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x4e, 0x20, 0x6e, - 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, - 0x72, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, 0x74, 0x65, 0x78, 0x74, - 0x60, 0x2e, 0x20, 0x42, 0x79, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x4e, - 0x20, 0x3d, 0x20, 0x35, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x77, 0x20, 0x61, 0x6e, 0x64, - 0x20, 0x68, 0x61, 0x72, 0x64, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, - 0x65, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x73, - 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x12, 0xd5, 0x02, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, 0x72, - 0x61, 0x70, 0x68, 0x12, 0x2c, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, - 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0xeb, 0x01, 0x92, 0x41, 0xc6, 0x01, 0x0a, 0x07, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x67, 0x65, - 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x11, 0x47, 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6e, - 0x65, 0x61, 0x67, 0x65, 0x20, 0x47, 0x72, 0x61, 0x70, 0x68, 0x1a, 0xa0, 0x01, 0x52, 0x65, 0x74, - 0x75, 0x72, 0x6e, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x67, 0x65, - 0x20, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2e, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, 0x65, 0x6e, 0x74, - 0x72, 0x79, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x72, 0x61, 0x70, 0x68, 0x20, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x73, 0x20, 0x61, 0x20, 0x28, 0x65, 0x64, 0x67, - 0x65, 0x29, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x72, 0x65, 0x6c, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x20, 0x77, - 0x69, 0x74, 0x68, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, - 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, - 0x20, 0x69, 0x74, 0x27, 0x73, 0x20, 0x75, 0x72, 0x6e, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, - 0x20, 0x61, 0x6e, 0x64, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6c, 0x69, - 0x6e, 0x65, 0x61, 0x67, 0x65, 0x2f, 0x7b, 0x75, 0x72, 0x6e, 0x3d, 0x2a, 0x2a, 0x7d, 0x12, 0xc8, - 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x2f, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x71, 0x92, + 0x41, 0x34, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x0a, 0x07, + 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x47, 0x65, 0x74, 0x20, 0x61, 0x20, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x64, 0x69, 0x73, 0x63, + 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x12, 0x32, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x2f, 0x7b, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, + 0x12, 0xef, 0x01, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, + 0x6e, 0x74, 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, 0x92, 0x41, 0x37, 0x0a, 0x0a, + 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x74, 0x12, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x37, 0x3a, 0x01, 0x2a, 0x1a, 0x32, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, + 0x64, 0x7d, 0x12, 0xec, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6d, + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x74, 0x92, 0x41, 0x37, + 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x0a, 0x07, 0x43, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x64, 0x69, 0x73, + 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x2a, 0x32, 0x2f, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x64, 0x7d, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, + 0x7d, 0x12, 0xc2, 0x06, 0x0a, 0x0c, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xcc, 0x05, 0x92, 0x41, 0xb1, 0x05, 0x0a, 0x06, + 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x13, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x1a, 0x8a, 0x05, 0x41, 0x50, 0x49, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, + 0x20, 0x27, 0x74, 0x65, 0x78, 0x74, 0x27, 0x20, 0x69, 0x73, 0x20, 0x66, 0x75, 0x7a, 0x7a, 0x79, + 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x73, 0x74, + 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x6c, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x73, 0x65, 0x74, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x20, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x20, 0x61, 0x72, 0x65, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x2e, 0x20, 0x59, + 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x61, + 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, + 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x27, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x2e, 0x2a, 0x5d, 0x27, 0x20, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x20, 0x59, 0x6f, + 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x65, 0x61, + 0x63, 0x68, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, + 0x6c, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x63, + 0x69, 0x66, 0x79, 0x20, 0x61, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65, 0x20, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x2c, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, + 0x74, 0x77, 0x6f, 0x20, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x27, 0x76, + 0x6e, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x27, 0x74, 0x68, 0x27, 0x2c, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, + 0x60, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x3f, 0x74, 0x65, 0x78, 0x74, 0x3d, 0x3c, + 0x74, 0x65, 0x78, 0x74, 0x3e, 0x26, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x65, 0x6e, 0x76, + 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5d, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x26, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x6c, 0x61, 0x6e, + 0x64, 0x73, 0x63, 0x61, 0x70, 0x65, 0x5d, 0x3d, 0x76, 0x6e, 0x2c, 0x74, 0x68, 0x60, 0x2e, 0x20, + 0x41, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, + 0x65, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x41, 0x50, 0x49, 0x20, 0x61, 0x6c, 0x73, 0x6f, + 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x73, 0x20, 0x66, 0x75, 0x7a, 0x7a, 0x79, 0x20, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x20, 0x27, 0x71, 0x75, 0x65, 0x72, 0x79, 0x27, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x20, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x2c, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x20, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x61, 0x73, 0x20, + 0x27, 0x62, 0x69, 0x67, 0x71, 0x75, 0x27, 0x20, 0x74, 0x65, 0x72, 0x6d, 0x20, 0x69, 0x6e, 0x20, + 0x69, 0x74, 0x73, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x60, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x3f, 0x74, 0x65, 0x78, 0x74, 0x3d, 0x3c, + 0x74, 0x65, 0x78, 0x74, 0x3e, 0x26, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5d, 0x3d, 0x62, 0x69, 0x67, 0x71, 0x75, 0x60, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0xa9, 0x08, 0x0a, 0x0b, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xb6, 0x07, 0x92, 0x41, 0x96, 0x07, + 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x14, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x61, 0x70, 0x69, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x1a, 0xef, 0x06, 0x41, 0x50, 0x49, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x20, 0x27, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x62, 0x79, 0x27, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, + 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x69, 0x6e, 0x67, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, + 0x61, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, + 0x70, 0x6c, 0x65, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x20, 0x61, 0x73, 0x20, 0x77, 0x65, + 0x6c, 0x6c, 0x2e, 0x20, 0x49, 0x66, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x6e, + 0x65, 0x73, 0x74, 0x65, 0x64, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x2c, 0x20, 0x79, 0x6f, + 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, 0x74, 0x68, + 0x65, 0x20, 0x70, 0x61, 0x74, 0x68, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x73, 0x70, 0x65, + 0x63, 0x69, 0x66, 0x79, 0x20, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x20, + 0x63, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x27, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x2e, 0x2a, 0x5d, 0x27, 0x20, 0x74, 0x6f, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65, 0x20, + 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x20, 0x59, 0x6f, 0x75, 0x20, 0x63, + 0x61, 0x6e, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x65, 0x61, 0x63, 0x68, 0x20, + 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, + 0x74, 0x69, 0x6d, 0x65, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, + 0x20, 0x61, 0x20, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, + 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x73, 0x2e, 0x20, 0x46, 0x6f, 0x72, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x2c, 0x20, 0x74, 0x6f, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x74, 0x77, 0x6f, + 0x20, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x27, 0x76, 0x6e, 0x27, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x27, 0x74, 0x68, 0x27, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x60, 0x2f, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x3f, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x62, 0x79, 0x3d, 0x3c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x62, 0x79, 0x3e, 0x26, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, + 0x5d, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x26, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x6c, 0x61, 0x6e, 0x64, 0x73, 0x63, 0x61, 0x70, 0x65, 0x5d, 0x3d, + 0x76, 0x6e, 0x2c, 0x74, 0x68, 0x60, 0x2e, 0x20, 0x27, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x27, 0x20, 0x69, 0x73, 0x20, 0x75, 0x73, 0x65, 0x64, + 0x20, 0x74, 0x6f, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x74, 0x68, + 0x65, 0x73, 0x65, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x72, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x68, 0x65, 0x6c, + 0x70, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69, 0x6e, + 0x67, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x20, + 0x45, 0x67, 0x3a, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, + 0x3f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x62, 0x79, 0x3d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x31, 0x26, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x62, 0x79, 0x3d, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x32, 0x26, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x5b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x5d, 0x3d, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x26, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x3d, 0x64, 0x61, 0x74, + 0x61, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x64, 0x61, 0x74, + 0x61, 0x73, 0x65, 0x74, 0x26, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x3d, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x20, 0x62, 0x79, 0x20, 0x77, + 0x69, 0x6c, 0x6c, 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, 0x62, 0x65, 0x20, 0x70, 0x61, + 0x72, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2c, 0x20, + 0x6e, 0x6f, 0x20, 0x6e, 0x65, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x64, 0x64, 0x20, 0x74, + 0x68, 0x65, 0x6d, 0x20, 0x69, 0x6e, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x73, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x12, 0xbc, 0x02, 0x0a, 0x0d, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x12, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xc3, 0x01, 0x92, 0x41, + 0xa0, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x12, 0x10, 0x53, 0x75, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x1a, 0x7d, 0x41, 0x50, 0x49, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65, 0x74, + 0x72, 0x65, 0x69, 0x76, 0x69, 0x6e, 0x67, 0x20, 0x4e, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x20, 0x6f, 0x66, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x20, 0x77, 0x69, 0x74, + 0x68, 0x20, 0x74, 0x68, 0x65, 0x20, 0x60, 0x74, 0x65, 0x78, 0x74, 0x60, 0x2e, 0x20, 0x42, 0x79, + 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x20, 0x4e, 0x20, 0x3d, 0x20, 0x35, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x77, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x61, 0x72, 0x64, + 0x63, 0x6f, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x64, + 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x73, 0x75, 0x67, 0x67, 0x65, 0x73, + 0x74, 0x12, 0xd5, 0x02, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x2c, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x56, 0x92, 0x41, 0x3d, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x66, 0x65, - 0x74, 0x63, 0x68, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x24, 0x46, - 0x65, 0x74, 0x63, 0x68, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x73, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6d, 0x70, - 0x61, 0x73, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x12, 0xa0, 0x02, 0x0a, 0x0c, 0x47, 0x65, - 0x74, 0x41, 0x6c, 0x6c, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x6f, 0x74, + 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x47, 0x72, + 0x61, 0x70, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xeb, 0x01, 0x92, 0x41, + 0xc6, 0x01, 0x0a, 0x07, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x67, 0x65, 0x0a, 0x05, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x12, 0x11, 0x47, 0x65, 0x74, 0x20, 0x4c, 0x69, 0x6e, 0x65, 0x61, 0x67, 0x65, 0x20, + 0x47, 0x72, 0x61, 0x70, 0x68, 0x1a, 0xa0, 0x01, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x67, 0x65, 0x20, 0x67, 0x72, 0x61, 0x70, + 0x68, 0x2e, 0x20, 0x45, 0x61, 0x63, 0x68, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x20, 0x69, 0x6e, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x67, 0x72, 0x61, 0x70, 0x68, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x73, 0x20, 0x61, 0x20, 0x28, 0x65, 0x64, 0x67, 0x65, 0x29, 0x20, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x65, 0x64, 0x20, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x6f, 0x66, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x27, 0x73, + 0x20, 0x75, 0x72, 0x6e, 0x2c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6c, 0x69, 0x6e, 0x65, 0x61, 0x67, 0x65, + 0x2f, 0x7b, 0x75, 0x72, 0x6e, 0x3d, 0x2a, 0x2a, 0x7d, 0x12, 0xc8, 0x01, 0x0a, 0x0b, 0x47, 0x65, + 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x79, + 0x70, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, + 0x79, 0x70, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x56, 0x92, 0x41, + 0x3d, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x20, 0x61, + 0x6c, 0x6c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x1a, 0x24, 0x46, 0x65, 0x74, 0x63, 0x68, 0x20, + 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, + 0x74, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x12, 0xa0, 0x02, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xaa, 0x01, 0x92, 0x41, 0x8f, + 0x01, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x12, 0x47, 0x65, 0x74, 0x20, 0x6c, 0x69, + 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x1a, 0x72, 0x52, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, + 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2c, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2c, 0x20, 0x73, 0x6f, + 0x72, 0x74, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x20, 0x69, 0x6e, + 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x71, 0x75, 0x65, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x20, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0xd0, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, + 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5b, 0x92, + 0x41, 0x3c, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x0d, 0x46, 0x69, 0x6e, 0x64, 0x20, + 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x24, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x49, 0x44, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xf9, 0x01, 0x0a, 0x0b, 0x55, + 0x70, 0x73, 0x65, 0x72, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x86, 0x01, + 0x92, 0x41, 0x69, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x16, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x2f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x1a, 0x48, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, + 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, + 0x6f, 0x72, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, + 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x66, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, + 0x6f, 0x74, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x20, 0x79, 0x65, 0x74, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x1a, 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x82, 0x02, 0x0a, 0x10, 0x55, 0x70, 0x73, 0x65, 0x72, + 0x74, 0x50, 0x61, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x34, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, + 0x50, 0x61, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x50, 0x61, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x80, 0x01, 0x92, 0x41, 0x63, 0x0a, 0x05, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x15, 0x50, 0x61, 0x74, 0x63, 0x68, 0x2f, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x43, 0x53, 0x69, + 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x20, + 0x62, 0x75, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x70, 0x61, 0x74, 0x63, 0x68, 0x20, 0x73, + 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x66, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x32, 0x0f, 0x2f, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0xce, 0x01, 0x0a, 0x0b, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, - 0x6c, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0xaa, 0x01, 0x92, 0x41, 0x8f, 0x01, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x12, 0x47, - 0x65, 0x74, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x73, 0x1a, 0x72, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, - 0x6f, 0x66, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2c, 0x20, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x62, - 0x79, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2c, 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x73, 0x2c, 0x20, 0x73, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x2e, 0x64, 0x61, 0x74, 0x61, - 0x20, 0x61, 0x6e, 0x64, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x73, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12, 0x0f, 0x2f, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0xd0, 0x01, 0x0a, - 0x0c, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x49, 0x44, 0x12, 0x30, 0x2e, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5c, + 0x92, 0x41, 0x3d, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x0f, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x23, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, 0x49, 0x44, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x2a, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xf0, 0x01, 0x0a, + 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x5b, 0x92, 0x41, 0x3c, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x0d, - 0x46, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x24, 0x52, - 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, - 0x20, 0x49, 0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, - 0xf9, 0x01, 0x0a, 0x0b, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, - 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, - 0x73, 0x65, 0x72, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, - 0x70, 0x73, 0x65, 0x72, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x86, 0x01, 0x92, 0x41, 0x69, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, - 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, - 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x48, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x20, - 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, - 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x69, 0x66, 0x20, 0x69, 0x74, 0x20, 0x64, - 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x20, 0x79, 0x65, - 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x1a, 0x0f, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x82, 0x02, 0x0a, 0x10, - 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x50, 0x61, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, - 0x70, 0x73, 0x65, 0x72, 0x74, 0x50, 0x61, 0x74, 0x63, 0x68, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x7b, 0x92, 0x41, 0x4e, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x0d, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x1a, 0x36, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x20, 0x65, 0x78, 0x70, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, 0x2a, 0x22, 0x1f, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, + 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x2d, 0x62, 0x79, 0x2d, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, + 0x85, 0x02, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, + 0x67, 0x61, 0x7a, 0x65, 0x72, 0x73, 0x12, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x50, 0x61, 0x74, 0x63, 0x68, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x80, 0x01, - 0x92, 0x41, 0x63, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x15, 0x50, 0x61, 0x74, 0x63, - 0x68, 0x2f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x1a, 0x43, 0x53, 0x69, 0x6d, 0x69, 0x6c, 0x61, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x55, 0x70, - 0x73, 0x65, 0x72, 0x74, 0x20, 0x62, 0x75, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x70, 0x61, - 0x74, 0x63, 0x68, 0x20, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x20, 0x61, 0x6e, 0x64, - 0x20, 0x64, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, - 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x32, - 0x0f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x12, 0xce, 0x01, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x12, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x5c, 0x92, 0x41, 0x3d, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, - 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, - 0x1a, 0x23, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, - 0x65, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x67, 0x69, 0x76, - 0x65, 0x6e, 0x20, 0x49, 0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x2a, 0x14, 0x2f, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, - 0x7d, 0x12, 0x85, 0x02, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x74, - 0x61, 0x72, 0x67, 0x61, 0x7a, 0x65, 0x72, 0x73, 0x12, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, - 0x74, 0x61, 0x72, 0x67, 0x61, 0x7a, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x67, 0x61, 0x7a, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7e, 0x92, 0x41, 0x54, 0x0a, 0x05, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x20, 0x75, 0x73, 0x65, 0x72, - 0x73, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x20, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x2b, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, - 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x72, 0x73, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x12, 0x1f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x73, - 0x74, 0x61, 0x72, 0x67, 0x61, 0x7a, 0x65, 0x72, 0x73, 0x12, 0x8c, 0x02, 0x0a, 0x16, 0x47, 0x65, - 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, - 0x74, 0x6f, 0x72, 0x79, 0x12, 0x3a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x3b, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x69, - 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x79, 0x92, - 0x41, 0x51, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x47, 0x65, 0x74, 0x20, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x6f, - 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x27, 0x52, 0x65, 0x74, 0x75, - 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x69, 0x73, 0x74, - 0x6f, 0x72, 0x79, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x83, 0x02, 0x0a, 0x11, 0x47, 0x65, 0x74, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x35, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x74, 0x61, + 0x72, 0x67, 0x61, 0x7a, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7f, 0x92, - 0x41, 0x4d, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1c, 0x47, 0x65, 0x74, 0x20, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x27, 0x73, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x26, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, - 0x20, 0x61, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x12, 0x27, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x7d, 0x12, 0xef, - 0x01, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x72, - 0x6f, 0x62, 0x65, 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, - 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x72, 0x6f, - 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, - 0x73, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x6e, 0x92, 0x41, 0x3a, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x14, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x27, 0x73, 0x20, 0x70, 0x72, 0x6f, - 0x62, 0x65, 0x1a, 0x1b, 0x41, 0x64, 0x64, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x70, 0x72, - 0x6f, 0x62, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x3a, 0x05, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x22, 0x22, 0x2f, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x5f, 0x75, 0x72, 0x6e, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x73, - 0x12, 0xe6, 0x01, 0x0a, 0x0a, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, - 0x2e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x79, - 0x6e, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x79, - 0x6e, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x77, 0x92, 0x41, 0x55, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1d, 0x53, 0x79, - 0x6e, 0x63, 0x73, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x65, 0x6c, - 0x61, 0x73, 0x74, 0x69, 0x63, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x1a, 0x2d, 0x53, 0x79, 0x6e, - 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x42, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6c, 0x61, - 0x73, 0x74, 0x69, 0x63, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, - 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x84, 0x02, 0x0a, 0x14, 0x47, 0x65, - 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, - 0x74, 0x73, 0x12, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x74, 0x61, 0x72, 0x67, 0x61, 0x7a, 0x65, 0x72, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7e, 0x92, 0x41, 0x54, 0x0a, 0x05, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x12, 0x1e, 0x46, 0x69, 0x6e, 0x64, 0x20, 0x75, 0x73, 0x65, 0x72, 0x73, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x1a, 0x2b, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x20, 0x6c, + 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x75, 0x73, 0x65, 0x72, 0x73, 0x20, 0x74, 0x68, 0x61, + 0x74, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x12, 0x1f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x74, 0x61, + 0x72, 0x67, 0x61, 0x7a, 0x65, 0x72, 0x73, 0x12, 0x8c, 0x02, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, 0x6f, + 0x72, 0x79, 0x12, 0x3a, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, - 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x67, + 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, + 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x79, 0x92, 0x41, 0x51, + 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1f, 0x47, 0x65, 0x74, 0x20, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x20, 0x6f, 0x66, 0x20, + 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x27, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x73, 0x20, 0x61, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, + 0x79, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x83, 0x02, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x42, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, - 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, 0x92, 0x41, 0x4c, 0x0a, 0x04, 0x55, 0x73, - 0x65, 0x72, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x72, 0x12, 0x1c, 0x47, 0x65, 0x74, 0x20, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x73, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, - 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, 0x1a, 0x20, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x62, - 0x79, 0x20, 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, - 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, - 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, - 0x12, 0xe6, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, - 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x42, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x73, 0x73, 0x65, 0x74, 0x42, 0x79, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7f, 0x92, 0x41, 0x4d, + 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1c, 0x47, 0x65, 0x74, 0x20, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x27, 0x73, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x20, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x26, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, + 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x29, 0x12, 0x27, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x2f, 0x7b, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x7d, 0x12, 0xef, 0x01, 0x0a, + 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x62, + 0x65, 0x12, 0x34, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, - 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x41, 0x0a, 0x04, 0x55, - 0x73, 0x65, 0x72, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x72, 0x12, 0x15, 0x47, 0x65, 0x74, 0x20, 0x6d, - 0x79, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x1a, 0x1c, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6d, 0x65, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6d, - 0x65, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x12, 0xeb, 0x01, 0x0a, 0x11, 0x47, 0x65, - 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, - 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, - 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, - 0x92, 0x41, 0x3e, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x72, 0x12, - 0x14, 0x47, 0x65, 0x74, 0x20, 0x6d, 0x79, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x1a, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6d, - 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, - 0x31, 0x2f, 0x6d, 0x65, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x2f, 0x7b, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0xcb, 0x01, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x72, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x2d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6e, + 0x92, 0x41, 0x3a, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x14, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x27, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x62, 0x65, + 0x1a, 0x1b, 0x41, 0x64, 0x64, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x70, 0x72, 0x6f, 0x62, + 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x2b, 0x3a, 0x05, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x5f, 0x75, 0x72, 0x6e, 0x7d, 0x2f, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x73, 0x12, 0xe6, + 0x01, 0x0a, 0x0a, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x2e, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, + 0x92, 0x41, 0x55, 0x0a, 0x05, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x1d, 0x53, 0x79, 0x6e, 0x63, + 0x73, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x65, 0x6c, 0x61, 0x73, + 0x74, 0x69, 0x63, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x1a, 0x2d, 0x53, 0x79, 0x6e, 0x63, 0x68, + 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x42, 0x20, 0x74, 0x6f, 0x20, 0x65, 0x6c, 0x61, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, + 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x12, 0x84, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x55, + 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x12, 0x38, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, + 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x67, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, + 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x77, 0x92, 0x41, 0x4c, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, + 0x0a, 0x04, 0x53, 0x74, 0x61, 0x72, 0x12, 0x1c, 0x47, 0x65, 0x74, 0x20, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x61, 0x20, + 0x75, 0x73, 0x65, 0x72, 0x1a, 0x20, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, + 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x2f, 0x7b, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x12, 0xe6, + 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, + 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x41, 0x0a, 0x04, 0x55, 0x73, 0x65, + 0x72, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x72, 0x12, 0x15, 0x47, 0x65, 0x74, 0x20, 0x6d, 0x79, 0x20, + 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x1a, 0x1c, + 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x20, 0x73, + 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6d, 0x65, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x15, 0x12, 0x13, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6d, 0x65, 0x2f, + 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x12, 0xeb, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, + 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x35, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, + 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x36, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x0a, - 0x04, 0x53, 0x74, 0x61, 0x72, 0x12, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x1a, 0x19, 0x4d, 0x61, 0x72, 0x6b, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x73, 0x74, 0x61, 0x72, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x1a, 0x1e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, + 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x53, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x67, 0x92, 0x41, + 0x3e, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x72, 0x12, 0x14, 0x47, + 0x65, 0x74, 0x20, 0x6d, 0x79, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x1a, 0x1a, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x6d, 0x65, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6d, 0x65, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x2f, 0x7b, 0x61, 0x73, 0x73, 0x65, - 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0xd1, 0x01, 0x0a, 0x0b, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, - 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x36, 0x0a, 0x04, 0x55, - 0x73, 0x65, 0x72, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x72, 0x12, 0x0f, 0x55, 0x6e, 0x73, 0x74, 0x61, - 0x72, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x17, 0x55, 0x6e, 0x6d, 0x61, - 0x72, 0x6b, 0x20, 0x6d, 0x79, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x61, 0x73, - 0x73, 0x65, 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x2a, 0x1e, 0x2f, 0x76, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x2f, 0x6d, 0x65, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x2f, 0x7b, - 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x8f, 0x02, 0x0a, 0x10, 0x47, 0x65, - 0x74, 0x4d, 0x79, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x34, - 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x4d, 0x79, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0xcb, 0x01, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x72, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x12, 0x2d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x2e, 0x53, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x36, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x0a, 0x04, 0x53, + 0x74, 0x61, 0x72, 0x12, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x1a, 0x19, 0x4d, 0x61, 0x72, 0x6b, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x61, 0x20, 0x73, 0x74, 0x61, 0x72, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x20, 0x1a, 0x1e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6d, 0x65, + 0x2f, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x2f, 0x7b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x69, 0x64, 0x7d, 0x12, 0xd1, 0x01, 0x0a, 0x0b, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x12, 0x2f, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8d, 0x01, 0x92, 0x41, - 0x6b, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x64, 0x69, 0x73, - 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x75, 0x73, - 0x65, 0x72, 0x1a, 0x38, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, - 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65, - 0x6e, 0x20, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6d, 0x65, 0x2f, - 0x64, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xd2, 0x01, 0x0a, 0x0e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x32, - 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, + 0x61, 0x31, 0x2e, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x36, 0x0a, 0x04, 0x55, 0x73, 0x65, + 0x72, 0x0a, 0x04, 0x53, 0x74, 0x61, 0x72, 0x12, 0x0f, 0x55, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x20, + 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x17, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x6b, + 0x20, 0x6d, 0x79, 0x20, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x20, 0x61, 0x73, 0x73, 0x65, + 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x2a, 0x1e, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, + 0x31, 0x2f, 0x6d, 0x65, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x72, 0x65, 0x64, 0x2f, 0x7b, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x8f, 0x02, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, + 0x79, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x34, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, + 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x57, 0x92, 0x41, 0x35, 0x0a, 0x03, 0x54, 0x61, - 0x67, 0x12, 0x0c, 0x54, 0x61, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, - 0x20, 0x54, 0x61, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x77, 0x69, - 0x74, 0x68, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x62, - 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, - 0x12, 0xb7, 0x02, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x42, 0x79, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x41, 0x6e, 0x64, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x2e, - 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, - 0x61, 0x67, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6e, 0x64, 0x54, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x2e, 0x67, 0x6f, + 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8d, 0x01, 0x92, 0x41, 0x6b, 0x0a, + 0x04, 0x55, 0x73, 0x65, 0x72, 0x0a, 0x0a, 0x44, 0x69, 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x1d, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x64, 0x69, 0x73, 0x63, 0x75, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, + 0x1a, 0x38, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x64, 0x69, + 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65, 0x6e, 0x20, + 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, + 0x20, 0x6f, 0x66, 0x20, 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, + 0x12, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x6d, 0x65, 0x2f, 0x64, 0x69, + 0x73, 0x63, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xd2, 0x01, 0x0a, 0x0e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x32, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x57, 0x92, 0x41, 0x35, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, + 0x0c, 0x54, 0x61, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x20, 0x54, + 0x61, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x3a, 0x01, 0x2a, 0x22, 0x14, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x12, 0xb7, + 0x02, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x41, 0x6e, 0x64, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6e, 0x64, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9d, 0x01, 0x92, 0x41, 0x5a, - 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x20, 0x46, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x74, 0x61, - 0x67, 0x20, 0x62, 0x79, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x1a, 0x31, 0x46, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x20, - 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x61, 0x67, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, - 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x69, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x20, 0x75, 0x72, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3a, - 0x12, 0x38, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x42, 0x79, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x41, 0x6e, 0x64, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9d, 0x01, 0x92, 0x41, 0x5a, 0x0a, 0x03, + 0x54, 0x61, 0x67, 0x12, 0x20, 0x46, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, + 0x62, 0x79, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x1a, 0x31, 0x46, 0x69, 0x6e, 0x64, 0x20, 0x61, 0x20, 0x73, 0x69, + 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x61, 0x67, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x61, + 0x73, 0x73, 0x65, 0x74, 0x20, 0x69, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x20, 0x75, 0x72, 0x6e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3a, 0x12, 0x38, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x61, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x7d, 0x12, 0xfa, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x32, 0x2e, 0x67, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, + 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7f, 0x92, 0x41, 0x39, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x18, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x6f, 0x6e, 0x20, + 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d, 0x3a, 0x01, 0x2a, 0x1a, 0x38, 0x2f, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x2f, 0x7b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x5f, 0x75, 0x72, 0x6e, 0x7d, 0x12, 0x82, 0x02, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x32, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x86, 0x01, 0x92, 0x41, 0x43, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x18, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x6e, + 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x22, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x61, + 0x20, 0x74, 0x61, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, + 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3a, + 0x2a, 0x38, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x7d, 0x12, 0xfa, 0x01, 0x0a, 0x0e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x32, 0x2e, - 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, - 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x7f, 0x92, 0x41, 0x39, 0x0a, 0x03, 0x54, 0x61, 0x67, - 0x12, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x6f, - 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x18, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x61, - 0x73, 0x73, 0x65, 0x74, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3d, 0x3a, 0x01, 0x2a, 0x1a, 0x38, 0x2f, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x7d, 0x12, 0x82, 0x02, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x32, 0x2e, 0x67, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, - 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, - 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x86, 0x01, 0x92, 0x41, 0x43, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x18, - 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x6f, 0x6e, 0x20, - 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x1a, 0x22, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, - 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, - 0x65, 0x74, 0x20, 0x69, 0x6e, 0x20, 0x61, 0x20, 0x74, 0x79, 0x70, 0x65, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x3a, 0x2a, 0x38, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, - 0x73, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, - 0x69, 0x64, 0x7d, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x7d, 0x12, 0xe4, 0x01, 0x0a, - 0x11, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x42, 0x79, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x42, 0x79, 0x41, 0x73, 0x73, - 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, - 0x67, 0x73, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x60, 0x92, 0x41, 0x36, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x13, 0x47, 0x65, 0x74, - 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x27, 0x73, 0x20, 0x74, 0x61, 0x67, 0x73, - 0x1a, 0x1a, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x61, 0x67, 0x73, 0x20, 0x66, - 0x6f, 0x72, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x21, 0x12, 0x1f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, - 0x73, 0x2f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, - 0x69, 0x64, 0x7d, 0x12, 0xe6, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, - 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x36, 0x2e, 0x67, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, - 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, - 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x3d, - 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x15, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, - 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x1f, 0x47, 0x65, - 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, - 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, - 0x67, 0x73, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0xdc, 0x01, 0x0a, - 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x7d, 0x12, 0xe4, 0x01, 0x0a, 0x11, 0x47, + 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x73, + 0x42, 0x79, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x60, 0x92, 0x41, 0x36, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x13, 0x47, 0x65, 0x74, 0x20, 0x61, + 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x27, 0x73, 0x20, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x1a, + 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x61, 0x67, 0x73, 0x20, 0x66, 0x6f, 0x72, + 0x20, 0x61, 0x6e, 0x20, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, + 0x12, 0x1f, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x2f, 0x7b, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x64, + 0x7d, 0x12, 0xe6, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x54, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, + 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, + 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x37, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5f, 0x92, 0x41, 0x3d, 0x0a, 0x03, + 0x54, 0x61, 0x67, 0x12, 0x15, 0x47, 0x65, 0x74, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x74, 0x61, 0x67, + 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x1a, 0x1f, 0x47, 0x65, 0x74, 0x20, + 0x61, 0x6c, 0x6c, 0x20, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x61, + 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x19, 0x12, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, + 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0xdc, 0x01, 0x0a, 0x11, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x58, 0x92, 0x41, 0x33, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x20, 0x61, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x1a, 0x19, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x01, 0x2a, + 0x22, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0xe0, 0x01, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x32, 0x2e, 0x67, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, + 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, + 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x65, 0x92, 0x41, 0x34, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, + 0x12, 0x47, 0x65, 0x74, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x1a, 0x19, 0x47, 0x65, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, + 0x65, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, + 0x61, 0x67, 0x73, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x7d, 0x12, 0xf1, 0x01, 0x0a, + 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, - 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x58, 0x92, 0x41, 0x33, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x11, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x1a, 0x19, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x74, 0x61, 0x67, - 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, - 0x01, 0x2a, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, - 0x73, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x12, 0xe0, 0x01, 0x0a, 0x0e, - 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x32, - 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x65, 0x92, 0x41, 0x34, 0x0a, 0x03, 0x54, 0x61, - 0x67, 0x12, 0x12, 0x47, 0x65, 0x74, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x1a, 0x19, 0x47, 0x65, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, - 0x67, 0x6c, 0x65, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, - 0x7b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x7d, 0x12, 0xf1, - 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, - 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, - 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x67, 0x6f, - 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, - 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x6d, 0x92, 0x41, 0x39, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x11, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x1a, 0x1f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x73, - 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x3a, 0x01, 0x2a, 0x1a, 0x26, 0x2f, 0x76, 0x31, 0x62, + 0x65, 0x22, 0x6d, 0x92, 0x41, 0x39, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x11, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x1a, 0x1f, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x3a, 0x01, 0x2a, 0x1a, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, 0x6e, 0x7d, + 0x12, 0xef, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, + 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, + 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6b, 0x92, 0x41, 0x3a, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, + 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x1a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, + 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x2a, 0x26, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x75, 0x72, - 0x6e, 0x7d, 0x12, 0xef, 0x01, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, - 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x35, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, - 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x36, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x54, 0x61, 0x67, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x6b, 0x92, 0x41, 0x3a, 0x0a, 0x03, 0x54, 0x61, - 0x67, 0x12, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x20, 0x61, 0x20, 0x74, 0x61, 0x67, 0x20, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x1a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x20, 0x61, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x74, 0x61, 0x67, 0x20, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x2a, 0x26, 0x2f, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x73, 0x2f, 0x7b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, - 0x75, 0x72, 0x6e, 0x7d, 0x42, 0xb5, 0x05, 0x92, 0x41, 0xc3, 0x04, 0x12, 0x97, 0x01, 0x0a, 0x07, - 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x12, 0x3c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x6f, 0x75, 0x72, 0x20, 0x43, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x20, 0x41, 0x50, 0x49, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x67, - 0x52, 0x50, 0x43, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x67, 0x52, 0x50, 0x43, 0x2d, 0x47, 0x61, 0x74, - 0x65, 0x77, 0x61, 0x79, 0x2e, 0x2a, 0x47, 0x0a, 0x12, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x20, - 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x32, 0x2e, 0x30, 0x12, 0x31, 0x68, 0x74, 0x74, - 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x67, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2f, 0x62, 0x6c, 0x6f, - 0x62, 0x2f, 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, - 0x30, 0x2e, 0x32, 0x2e, 0x31, 0x2a, 0x02, 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, 0x52, 0x51, 0x0a, - 0x03, 0x34, 0x30, 0x30, 0x12, 0x4a, 0x0a, 0x30, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, - 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, - 0x68, 0x61, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x69, - 0x73, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x2e, 0x12, 0x16, 0x0a, 0x14, 0x1a, 0x12, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x4b, 0x0a, 0x03, 0x34, 0x30, 0x34, 0x12, 0x44, 0x0a, 0x2a, 0x52, 0x65, 0x74, 0x75, 0x72, - 0x6e, 0x65, 0x64, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, - 0x78, 0x69, 0x73, 0x74, 0x2e, 0x12, 0x16, 0x0a, 0x14, 0x1a, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x4a, 0x0a, - 0x03, 0x34, 0x30, 0x39, 0x12, 0x43, 0x0a, 0x29, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, - 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, - 0x2e, 0x12, 0x16, 0x0a, 0x14, 0x1a, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, - 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x5c, 0x0a, 0x03, 0x35, 0x30, 0x30, - 0x12, 0x55, 0x0a, 0x3b, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x77, 0x68, 0x65, - 0x6e, 0x20, 0x74, 0x68, 0x65, 0x72, 0x65, 0x73, 0x20, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65, - 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2e, 0x12, + 0x6e, 0x7d, 0x42, 0xb5, 0x05, 0x92, 0x41, 0xc3, 0x04, 0x12, 0x97, 0x01, 0x0a, 0x07, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x73, 0x73, 0x12, 0x3c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x6f, 0x75, 0x72, 0x20, 0x43, 0x6f, 0x6d, 0x70, + 0x61, 0x73, 0x73, 0x20, 0x41, 0x50, 0x49, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x67, 0x52, 0x50, + 0x43, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x67, 0x52, 0x50, 0x43, 0x2d, 0x47, 0x61, 0x74, 0x65, 0x77, + 0x61, 0x79, 0x2e, 0x2a, 0x47, 0x0a, 0x12, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x20, 0x4c, 0x69, + 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x32, 0x2e, 0x30, 0x12, 0x31, 0x68, 0x74, 0x74, 0x70, 0x73, + 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, + 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, + 0x6d, 0x61, 0x69, 0x6e, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x30, 0x2e, + 0x32, 0x2e, 0x31, 0x2a, 0x02, 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, 0x52, 0x51, 0x0a, 0x03, 0x34, + 0x30, 0x30, 0x12, 0x4a, 0x0a, 0x30, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x77, + 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x74, 0x68, 0x61, + 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x69, 0x73, 0x20, + 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x2e, 0x12, 0x16, 0x0a, 0x14, 0x1a, 0x12, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x4b, + 0x0a, 0x03, 0x34, 0x30, 0x34, 0x12, 0x44, 0x0a, 0x2a, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, + 0x64, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x65, 0x78, 0x69, + 0x73, 0x74, 0x2e, 0x12, 0x16, 0x0a, 0x14, 0x1a, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x4a, 0x0a, 0x03, 0x34, + 0x30, 0x39, 0x12, 0x43, 0x0a, 0x29, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x77, + 0x68, 0x65, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x20, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x65, 0x78, 0x69, 0x73, 0x74, 0x2e, 0x12, 0x16, 0x0a, 0x14, 0x1a, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x72, 0x35, 0x0a, 0x12, 0x4d, 0x6f, 0x72, 0x65, 0x20, - 0x61, 0x62, 0x6f, 0x75, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x12, 0x1f, 0x68, - 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2e, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x69, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2f, 0x0a, 0x1e, - 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x42, 0x13, - 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x63, 0x6f, - 0x6d, 0x70, 0x61, 0x73, 0x73, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x5c, 0x0a, 0x03, 0x35, 0x30, 0x30, 0x12, 0x55, + 0x0a, 0x3b, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x65, 0x64, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x73, 0x20, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x68, + 0x69, 0x6e, 0x67, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, + 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2e, 0x12, 0x16, 0x0a, + 0x14, 0x1a, 0x12, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x72, 0x35, 0x0a, 0x12, 0x4d, 0x6f, 0x72, 0x65, 0x20, 0x61, 0x62, + 0x6f, 0x75, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x12, 0x1f, 0x68, 0x74, 0x74, + 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x6f, 0x74, 0x6f, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x69, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x2f, 0x0a, 0x1e, 0x63, 0x6f, + 0x6d, 0x2e, 0x67, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x6e, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x73, 0x73, 0x42, 0x13, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x73, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x63, 0x6f, 0x6d, 0x70, + 0x61, 0x73, 0x73, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -8156,7 +8296,7 @@ func file_gotocompany_compass_v1beta1_service_proto_rawDescGZIP() []byte { return file_gotocompany_compass_v1beta1_service_proto_rawDescData } -var file_gotocompany_compass_v1beta1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 110) +var file_gotocompany_compass_v1beta1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 112) var file_gotocompany_compass_v1beta1_service_proto_goTypes = []interface{}{ (*GetAllDiscussionsRequest)(nil), // 0: gotocompany.compass.v1beta1.GetAllDiscussionsRequest (*GetAllDiscussionsResponse)(nil), // 1: gotocompany.compass.v1beta1.GetAllDiscussionsResponse @@ -8199,171 +8339,173 @@ var file_gotocompany_compass_v1beta1_service_proto_goTypes = []interface{}{ (*UpsertPatchAssetResponse)(nil), // 38: gotocompany.compass.v1beta1.UpsertPatchAssetResponse (*DeleteAssetRequest)(nil), // 39: gotocompany.compass.v1beta1.DeleteAssetRequest (*DeleteAssetResponse)(nil), // 40: gotocompany.compass.v1beta1.DeleteAssetResponse - (*GetAssetStargazersRequest)(nil), // 41: gotocompany.compass.v1beta1.GetAssetStargazersRequest - (*GetAssetStargazersResponse)(nil), // 42: gotocompany.compass.v1beta1.GetAssetStargazersResponse - (*GetAssetVersionHistoryRequest)(nil), // 43: gotocompany.compass.v1beta1.GetAssetVersionHistoryRequest - (*GetAssetVersionHistoryResponse)(nil), // 44: gotocompany.compass.v1beta1.GetAssetVersionHistoryResponse - (*GetAssetByVersionRequest)(nil), // 45: gotocompany.compass.v1beta1.GetAssetByVersionRequest - (*GetAssetByVersionResponse)(nil), // 46: gotocompany.compass.v1beta1.GetAssetByVersionResponse - (*CreateAssetProbeRequest)(nil), // 47: gotocompany.compass.v1beta1.CreateAssetProbeRequest - (*CreateAssetProbeResponse)(nil), // 48: gotocompany.compass.v1beta1.CreateAssetProbeResponse - (*SyncAssetsRequest)(nil), // 49: gotocompany.compass.v1beta1.SyncAssetsRequest - (*SyncAssetsResponse)(nil), // 50: gotocompany.compass.v1beta1.SyncAssetsResponse - (*GetUserStarredAssetsRequest)(nil), // 51: gotocompany.compass.v1beta1.GetUserStarredAssetsRequest - (*GetUserStarredAssetsResponse)(nil), // 52: gotocompany.compass.v1beta1.GetUserStarredAssetsResponse - (*GetMyStarredAssetsRequest)(nil), // 53: gotocompany.compass.v1beta1.GetMyStarredAssetsRequest - (*GetMyStarredAssetsResponse)(nil), // 54: gotocompany.compass.v1beta1.GetMyStarredAssetsResponse - (*GetMyStarredAssetRequest)(nil), // 55: gotocompany.compass.v1beta1.GetMyStarredAssetRequest - (*GetMyStarredAssetResponse)(nil), // 56: gotocompany.compass.v1beta1.GetMyStarredAssetResponse - (*StarAssetRequest)(nil), // 57: gotocompany.compass.v1beta1.StarAssetRequest - (*StarAssetResponse)(nil), // 58: gotocompany.compass.v1beta1.StarAssetResponse - (*UnstarAssetRequest)(nil), // 59: gotocompany.compass.v1beta1.UnstarAssetRequest - (*UnstarAssetResponse)(nil), // 60: gotocompany.compass.v1beta1.UnstarAssetResponse - (*GetMyDiscussionsRequest)(nil), // 61: gotocompany.compass.v1beta1.GetMyDiscussionsRequest - (*GetMyDiscussionsResponse)(nil), // 62: gotocompany.compass.v1beta1.GetMyDiscussionsResponse - (*CreateTagAssetRequest)(nil), // 63: gotocompany.compass.v1beta1.CreateTagAssetRequest - (*CreateTagAssetResponse)(nil), // 64: gotocompany.compass.v1beta1.CreateTagAssetResponse - (*GetTagByAssetAndTemplateRequest)(nil), // 65: gotocompany.compass.v1beta1.GetTagByAssetAndTemplateRequest - (*GetTagByAssetAndTemplateResponse)(nil), // 66: gotocompany.compass.v1beta1.GetTagByAssetAndTemplateResponse - (*UpdateTagAssetRequest)(nil), // 67: gotocompany.compass.v1beta1.UpdateTagAssetRequest - (*UpdateTagAssetResponse)(nil), // 68: gotocompany.compass.v1beta1.UpdateTagAssetResponse - (*DeleteTagAssetRequest)(nil), // 69: gotocompany.compass.v1beta1.DeleteTagAssetRequest - (*DeleteTagAssetResponse)(nil), // 70: gotocompany.compass.v1beta1.DeleteTagAssetResponse - (*GetAllTagsByAssetRequest)(nil), // 71: gotocompany.compass.v1beta1.GetAllTagsByAssetRequest - (*GetAllTagsByAssetResponse)(nil), // 72: gotocompany.compass.v1beta1.GetAllTagsByAssetResponse - (*GetAllTagTemplatesRequest)(nil), // 73: gotocompany.compass.v1beta1.GetAllTagTemplatesRequest - (*GetAllTagTemplatesResponse)(nil), // 74: gotocompany.compass.v1beta1.GetAllTagTemplatesResponse - (*CreateTagTemplateRequest)(nil), // 75: gotocompany.compass.v1beta1.CreateTagTemplateRequest - (*CreateTagTemplateResponse)(nil), // 76: gotocompany.compass.v1beta1.CreateTagTemplateResponse - (*GetTagTemplateRequest)(nil), // 77: gotocompany.compass.v1beta1.GetTagTemplateRequest - (*GetTagTemplateResponse)(nil), // 78: gotocompany.compass.v1beta1.GetTagTemplateResponse - (*UpdateTagTemplateRequest)(nil), // 79: gotocompany.compass.v1beta1.UpdateTagTemplateRequest - (*UpdateTagTemplateResponse)(nil), // 80: gotocompany.compass.v1beta1.UpdateTagTemplateResponse - (*DeleteTagTemplateRequest)(nil), // 81: gotocompany.compass.v1beta1.DeleteTagTemplateRequest - (*DeleteTagTemplateResponse)(nil), // 82: gotocompany.compass.v1beta1.DeleteTagTemplateResponse - (*User)(nil), // 83: gotocompany.compass.v1beta1.User - (*Change)(nil), // 84: gotocompany.compass.v1beta1.Change - (*Asset)(nil), // 85: gotocompany.compass.v1beta1.Asset - (*Probe)(nil), // 86: gotocompany.compass.v1beta1.Probe - (*Discussion)(nil), // 87: gotocompany.compass.v1beta1.Discussion - (*Comment)(nil), // 88: gotocompany.compass.v1beta1.Comment - (*LineageEdge)(nil), // 89: gotocompany.compass.v1beta1.LineageEdge - (*LineageNode)(nil), // 90: gotocompany.compass.v1beta1.LineageNode - (*Tag)(nil), // 91: gotocompany.compass.v1beta1.Tag - (*TagValue)(nil), // 92: gotocompany.compass.v1beta1.TagValue - (*TagTemplate)(nil), // 93: gotocompany.compass.v1beta1.TagTemplate - (*TagTemplateField)(nil), // 94: gotocompany.compass.v1beta1.TagTemplateField - (*Type)(nil), // 95: gotocompany.compass.v1beta1.Type - nil, // 96: gotocompany.compass.v1beta1.SearchAssetsRequest.FilterEntry - nil, // 97: gotocompany.compass.v1beta1.SearchAssetsRequest.QueryEntry - nil, // 98: gotocompany.compass.v1beta1.GroupAssetsRequest.FilterEntry - (*GetGraphResponse_ProbesInfo)(nil), // 99: gotocompany.compass.v1beta1.GetGraphResponse.ProbesInfo - (*GetGraphResponse_NodeAttributes)(nil), // 100: gotocompany.compass.v1beta1.GetGraphResponse.NodeAttributes - nil, // 101: gotocompany.compass.v1beta1.GetGraphResponse.NodeAttrsEntry - nil, // 102: gotocompany.compass.v1beta1.GetAllTypesRequest.DataEntry - nil, // 103: gotocompany.compass.v1beta1.GetAllAssetsRequest.DataEntry - (*UpsertAssetRequest_Asset)(nil), // 104: gotocompany.compass.v1beta1.UpsertAssetRequest.Asset - nil, // 105: gotocompany.compass.v1beta1.UpsertAssetRequest.Asset.LabelsEntry - (*UpsertPatchAssetRequest_Asset)(nil), // 106: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset - nil, // 107: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.LabelsEntry - (*CreateAssetProbeRequest_Probe)(nil), // 108: gotocompany.compass.v1beta1.CreateAssetProbeRequest.Probe - nil, // 109: gotocompany.compass.v1beta1.Asset.LabelsEntry - (*timestamppb.Timestamp)(nil), // 110: google.protobuf.Timestamp - (*structpb.Value)(nil), // 111: google.protobuf.Value - (*structpb.Struct)(nil), // 112: google.protobuf.Struct - (*wrapperspb.StringValue)(nil), // 113: google.protobuf.StringValue + (*DeleteAssetsRequest)(nil), // 41: gotocompany.compass.v1beta1.DeleteAssetsRequest + (*DeleteAssetsResponse)(nil), // 42: gotocompany.compass.v1beta1.DeleteAssetsResponse + (*GetAssetStargazersRequest)(nil), // 43: gotocompany.compass.v1beta1.GetAssetStargazersRequest + (*GetAssetStargazersResponse)(nil), // 44: gotocompany.compass.v1beta1.GetAssetStargazersResponse + (*GetAssetVersionHistoryRequest)(nil), // 45: gotocompany.compass.v1beta1.GetAssetVersionHistoryRequest + (*GetAssetVersionHistoryResponse)(nil), // 46: gotocompany.compass.v1beta1.GetAssetVersionHistoryResponse + (*GetAssetByVersionRequest)(nil), // 47: gotocompany.compass.v1beta1.GetAssetByVersionRequest + (*GetAssetByVersionResponse)(nil), // 48: gotocompany.compass.v1beta1.GetAssetByVersionResponse + (*CreateAssetProbeRequest)(nil), // 49: gotocompany.compass.v1beta1.CreateAssetProbeRequest + (*CreateAssetProbeResponse)(nil), // 50: gotocompany.compass.v1beta1.CreateAssetProbeResponse + (*SyncAssetsRequest)(nil), // 51: gotocompany.compass.v1beta1.SyncAssetsRequest + (*SyncAssetsResponse)(nil), // 52: gotocompany.compass.v1beta1.SyncAssetsResponse + (*GetUserStarredAssetsRequest)(nil), // 53: gotocompany.compass.v1beta1.GetUserStarredAssetsRequest + (*GetUserStarredAssetsResponse)(nil), // 54: gotocompany.compass.v1beta1.GetUserStarredAssetsResponse + (*GetMyStarredAssetsRequest)(nil), // 55: gotocompany.compass.v1beta1.GetMyStarredAssetsRequest + (*GetMyStarredAssetsResponse)(nil), // 56: gotocompany.compass.v1beta1.GetMyStarredAssetsResponse + (*GetMyStarredAssetRequest)(nil), // 57: gotocompany.compass.v1beta1.GetMyStarredAssetRequest + (*GetMyStarredAssetResponse)(nil), // 58: gotocompany.compass.v1beta1.GetMyStarredAssetResponse + (*StarAssetRequest)(nil), // 59: gotocompany.compass.v1beta1.StarAssetRequest + (*StarAssetResponse)(nil), // 60: gotocompany.compass.v1beta1.StarAssetResponse + (*UnstarAssetRequest)(nil), // 61: gotocompany.compass.v1beta1.UnstarAssetRequest + (*UnstarAssetResponse)(nil), // 62: gotocompany.compass.v1beta1.UnstarAssetResponse + (*GetMyDiscussionsRequest)(nil), // 63: gotocompany.compass.v1beta1.GetMyDiscussionsRequest + (*GetMyDiscussionsResponse)(nil), // 64: gotocompany.compass.v1beta1.GetMyDiscussionsResponse + (*CreateTagAssetRequest)(nil), // 65: gotocompany.compass.v1beta1.CreateTagAssetRequest + (*CreateTagAssetResponse)(nil), // 66: gotocompany.compass.v1beta1.CreateTagAssetResponse + (*GetTagByAssetAndTemplateRequest)(nil), // 67: gotocompany.compass.v1beta1.GetTagByAssetAndTemplateRequest + (*GetTagByAssetAndTemplateResponse)(nil), // 68: gotocompany.compass.v1beta1.GetTagByAssetAndTemplateResponse + (*UpdateTagAssetRequest)(nil), // 69: gotocompany.compass.v1beta1.UpdateTagAssetRequest + (*UpdateTagAssetResponse)(nil), // 70: gotocompany.compass.v1beta1.UpdateTagAssetResponse + (*DeleteTagAssetRequest)(nil), // 71: gotocompany.compass.v1beta1.DeleteTagAssetRequest + (*DeleteTagAssetResponse)(nil), // 72: gotocompany.compass.v1beta1.DeleteTagAssetResponse + (*GetAllTagsByAssetRequest)(nil), // 73: gotocompany.compass.v1beta1.GetAllTagsByAssetRequest + (*GetAllTagsByAssetResponse)(nil), // 74: gotocompany.compass.v1beta1.GetAllTagsByAssetResponse + (*GetAllTagTemplatesRequest)(nil), // 75: gotocompany.compass.v1beta1.GetAllTagTemplatesRequest + (*GetAllTagTemplatesResponse)(nil), // 76: gotocompany.compass.v1beta1.GetAllTagTemplatesResponse + (*CreateTagTemplateRequest)(nil), // 77: gotocompany.compass.v1beta1.CreateTagTemplateRequest + (*CreateTagTemplateResponse)(nil), // 78: gotocompany.compass.v1beta1.CreateTagTemplateResponse + (*GetTagTemplateRequest)(nil), // 79: gotocompany.compass.v1beta1.GetTagTemplateRequest + (*GetTagTemplateResponse)(nil), // 80: gotocompany.compass.v1beta1.GetTagTemplateResponse + (*UpdateTagTemplateRequest)(nil), // 81: gotocompany.compass.v1beta1.UpdateTagTemplateRequest + (*UpdateTagTemplateResponse)(nil), // 82: gotocompany.compass.v1beta1.UpdateTagTemplateResponse + (*DeleteTagTemplateRequest)(nil), // 83: gotocompany.compass.v1beta1.DeleteTagTemplateRequest + (*DeleteTagTemplateResponse)(nil), // 84: gotocompany.compass.v1beta1.DeleteTagTemplateResponse + (*User)(nil), // 85: gotocompany.compass.v1beta1.User + (*Change)(nil), // 86: gotocompany.compass.v1beta1.Change + (*Asset)(nil), // 87: gotocompany.compass.v1beta1.Asset + (*Probe)(nil), // 88: gotocompany.compass.v1beta1.Probe + (*Discussion)(nil), // 89: gotocompany.compass.v1beta1.Discussion + (*Comment)(nil), // 90: gotocompany.compass.v1beta1.Comment + (*LineageEdge)(nil), // 91: gotocompany.compass.v1beta1.LineageEdge + (*LineageNode)(nil), // 92: gotocompany.compass.v1beta1.LineageNode + (*Tag)(nil), // 93: gotocompany.compass.v1beta1.Tag + (*TagValue)(nil), // 94: gotocompany.compass.v1beta1.TagValue + (*TagTemplate)(nil), // 95: gotocompany.compass.v1beta1.TagTemplate + (*TagTemplateField)(nil), // 96: gotocompany.compass.v1beta1.TagTemplateField + (*Type)(nil), // 97: gotocompany.compass.v1beta1.Type + nil, // 98: gotocompany.compass.v1beta1.SearchAssetsRequest.FilterEntry + nil, // 99: gotocompany.compass.v1beta1.SearchAssetsRequest.QueryEntry + nil, // 100: gotocompany.compass.v1beta1.GroupAssetsRequest.FilterEntry + (*GetGraphResponse_ProbesInfo)(nil), // 101: gotocompany.compass.v1beta1.GetGraphResponse.ProbesInfo + (*GetGraphResponse_NodeAttributes)(nil), // 102: gotocompany.compass.v1beta1.GetGraphResponse.NodeAttributes + nil, // 103: gotocompany.compass.v1beta1.GetGraphResponse.NodeAttrsEntry + nil, // 104: gotocompany.compass.v1beta1.GetAllTypesRequest.DataEntry + nil, // 105: gotocompany.compass.v1beta1.GetAllAssetsRequest.DataEntry + (*UpsertAssetRequest_Asset)(nil), // 106: gotocompany.compass.v1beta1.UpsertAssetRequest.Asset + nil, // 107: gotocompany.compass.v1beta1.UpsertAssetRequest.Asset.LabelsEntry + (*UpsertPatchAssetRequest_Asset)(nil), // 108: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset + nil, // 109: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.LabelsEntry + (*CreateAssetProbeRequest_Probe)(nil), // 110: gotocompany.compass.v1beta1.CreateAssetProbeRequest.Probe + nil, // 111: gotocompany.compass.v1beta1.Asset.LabelsEntry + (*timestamppb.Timestamp)(nil), // 112: google.protobuf.Timestamp + (*structpb.Value)(nil), // 113: google.protobuf.Value + (*structpb.Struct)(nil), // 114: google.protobuf.Struct + (*wrapperspb.StringValue)(nil), // 115: google.protobuf.StringValue } var file_gotocompany_compass_v1beta1_service_proto_depIdxs = []int32{ - 87, // 0: gotocompany.compass.v1beta1.GetAllDiscussionsResponse.data:type_name -> gotocompany.compass.v1beta1.Discussion - 87, // 1: gotocompany.compass.v1beta1.GetDiscussionResponse.data:type_name -> gotocompany.compass.v1beta1.Discussion - 88, // 2: gotocompany.compass.v1beta1.GetAllCommentsResponse.data:type_name -> gotocompany.compass.v1beta1.Comment - 88, // 3: gotocompany.compass.v1beta1.GetCommentResponse.data:type_name -> gotocompany.compass.v1beta1.Comment - 96, // 4: gotocompany.compass.v1beta1.SearchAssetsRequest.filter:type_name -> gotocompany.compass.v1beta1.SearchAssetsRequest.FilterEntry - 97, // 5: gotocompany.compass.v1beta1.SearchAssetsRequest.query:type_name -> gotocompany.compass.v1beta1.SearchAssetsRequest.QueryEntry + 89, // 0: gotocompany.compass.v1beta1.GetAllDiscussionsResponse.data:type_name -> gotocompany.compass.v1beta1.Discussion + 89, // 1: gotocompany.compass.v1beta1.GetDiscussionResponse.data:type_name -> gotocompany.compass.v1beta1.Discussion + 90, // 2: gotocompany.compass.v1beta1.GetAllCommentsResponse.data:type_name -> gotocompany.compass.v1beta1.Comment + 90, // 3: gotocompany.compass.v1beta1.GetCommentResponse.data:type_name -> gotocompany.compass.v1beta1.Comment + 98, // 4: gotocompany.compass.v1beta1.SearchAssetsRequest.filter:type_name -> gotocompany.compass.v1beta1.SearchAssetsRequest.FilterEntry + 99, // 5: gotocompany.compass.v1beta1.SearchAssetsRequest.query:type_name -> gotocompany.compass.v1beta1.SearchAssetsRequest.QueryEntry 23, // 6: gotocompany.compass.v1beta1.SearchAssetsRequest.flags:type_name -> gotocompany.compass.v1beta1.SearchFlags - 98, // 7: gotocompany.compass.v1beta1.GroupAssetsRequest.filter:type_name -> gotocompany.compass.v1beta1.GroupAssetsRequest.FilterEntry + 100, // 7: gotocompany.compass.v1beta1.GroupAssetsRequest.filter:type_name -> gotocompany.compass.v1beta1.GroupAssetsRequest.FilterEntry 21, // 8: gotocompany.compass.v1beta1.GroupAssetsResponse.asset_groups:type_name -> gotocompany.compass.v1beta1.AssetGroup 22, // 9: gotocompany.compass.v1beta1.AssetGroup.group_fields:type_name -> gotocompany.compass.v1beta1.GroupField - 85, // 10: gotocompany.compass.v1beta1.AssetGroup.assets:type_name -> gotocompany.compass.v1beta1.Asset - 85, // 11: gotocompany.compass.v1beta1.SearchAssetsResponse.data:type_name -> gotocompany.compass.v1beta1.Asset - 89, // 12: gotocompany.compass.v1beta1.GetGraphResponse.data:type_name -> gotocompany.compass.v1beta1.LineageEdge - 101, // 13: gotocompany.compass.v1beta1.GetGraphResponse.node_attrs:type_name -> gotocompany.compass.v1beta1.GetGraphResponse.NodeAttrsEntry - 102, // 14: gotocompany.compass.v1beta1.GetAllTypesRequest.data:type_name -> gotocompany.compass.v1beta1.GetAllTypesRequest.DataEntry - 95, // 15: gotocompany.compass.v1beta1.GetAllTypesResponse.data:type_name -> gotocompany.compass.v1beta1.Type - 103, // 16: gotocompany.compass.v1beta1.GetAllAssetsRequest.data:type_name -> gotocompany.compass.v1beta1.GetAllAssetsRequest.DataEntry - 85, // 17: gotocompany.compass.v1beta1.GetAllAssetsResponse.data:type_name -> gotocompany.compass.v1beta1.Asset - 85, // 18: gotocompany.compass.v1beta1.GetAssetByIDResponse.data:type_name -> gotocompany.compass.v1beta1.Asset - 104, // 19: gotocompany.compass.v1beta1.UpsertAssetRequest.asset:type_name -> gotocompany.compass.v1beta1.UpsertAssetRequest.Asset - 90, // 20: gotocompany.compass.v1beta1.UpsertAssetRequest.upstreams:type_name -> gotocompany.compass.v1beta1.LineageNode - 90, // 21: gotocompany.compass.v1beta1.UpsertAssetRequest.downstreams:type_name -> gotocompany.compass.v1beta1.LineageNode - 106, // 22: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.asset:type_name -> gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset - 90, // 23: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.upstreams:type_name -> gotocompany.compass.v1beta1.LineageNode - 90, // 24: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.downstreams:type_name -> gotocompany.compass.v1beta1.LineageNode - 83, // 25: gotocompany.compass.v1beta1.GetAssetStargazersResponse.data:type_name -> gotocompany.compass.v1beta1.User - 85, // 26: gotocompany.compass.v1beta1.GetAssetVersionHistoryResponse.data:type_name -> gotocompany.compass.v1beta1.Asset - 85, // 27: gotocompany.compass.v1beta1.GetAssetByVersionResponse.data:type_name -> gotocompany.compass.v1beta1.Asset - 108, // 28: gotocompany.compass.v1beta1.CreateAssetProbeRequest.probe:type_name -> gotocompany.compass.v1beta1.CreateAssetProbeRequest.Probe - 85, // 29: gotocompany.compass.v1beta1.GetUserStarredAssetsResponse.data:type_name -> gotocompany.compass.v1beta1.Asset - 85, // 30: gotocompany.compass.v1beta1.GetMyStarredAssetsResponse.data:type_name -> gotocompany.compass.v1beta1.Asset - 85, // 31: gotocompany.compass.v1beta1.GetMyStarredAssetResponse.data:type_name -> gotocompany.compass.v1beta1.Asset - 87, // 32: gotocompany.compass.v1beta1.GetMyDiscussionsResponse.data:type_name -> gotocompany.compass.v1beta1.Discussion - 92, // 33: gotocompany.compass.v1beta1.CreateTagAssetRequest.tag_values:type_name -> gotocompany.compass.v1beta1.TagValue - 91, // 34: gotocompany.compass.v1beta1.CreateTagAssetResponse.data:type_name -> gotocompany.compass.v1beta1.Tag - 91, // 35: gotocompany.compass.v1beta1.GetTagByAssetAndTemplateResponse.data:type_name -> gotocompany.compass.v1beta1.Tag - 92, // 36: gotocompany.compass.v1beta1.UpdateTagAssetRequest.tag_values:type_name -> gotocompany.compass.v1beta1.TagValue - 91, // 37: gotocompany.compass.v1beta1.UpdateTagAssetResponse.data:type_name -> gotocompany.compass.v1beta1.Tag - 91, // 38: gotocompany.compass.v1beta1.GetAllTagsByAssetResponse.data:type_name -> gotocompany.compass.v1beta1.Tag - 93, // 39: gotocompany.compass.v1beta1.GetAllTagTemplatesResponse.data:type_name -> gotocompany.compass.v1beta1.TagTemplate - 94, // 40: gotocompany.compass.v1beta1.CreateTagTemplateRequest.fields:type_name -> gotocompany.compass.v1beta1.TagTemplateField - 93, // 41: gotocompany.compass.v1beta1.CreateTagTemplateResponse.data:type_name -> gotocompany.compass.v1beta1.TagTemplate - 93, // 42: gotocompany.compass.v1beta1.GetTagTemplateResponse.data:type_name -> gotocompany.compass.v1beta1.TagTemplate - 94, // 43: gotocompany.compass.v1beta1.UpdateTagTemplateRequest.fields:type_name -> gotocompany.compass.v1beta1.TagTemplateField - 93, // 44: gotocompany.compass.v1beta1.UpdateTagTemplateResponse.data:type_name -> gotocompany.compass.v1beta1.TagTemplate - 110, // 45: gotocompany.compass.v1beta1.User.created_at:type_name -> google.protobuf.Timestamp - 110, // 46: gotocompany.compass.v1beta1.User.updated_at:type_name -> google.protobuf.Timestamp - 111, // 47: gotocompany.compass.v1beta1.Change.from:type_name -> google.protobuf.Value - 111, // 48: gotocompany.compass.v1beta1.Change.to:type_name -> google.protobuf.Value - 112, // 49: gotocompany.compass.v1beta1.Asset.data:type_name -> google.protobuf.Struct - 109, // 50: gotocompany.compass.v1beta1.Asset.labels:type_name -> gotocompany.compass.v1beta1.Asset.LabelsEntry - 83, // 51: gotocompany.compass.v1beta1.Asset.owners:type_name -> gotocompany.compass.v1beta1.User - 83, // 52: gotocompany.compass.v1beta1.Asset.updated_by:type_name -> gotocompany.compass.v1beta1.User - 84, // 53: gotocompany.compass.v1beta1.Asset.changelog:type_name -> gotocompany.compass.v1beta1.Change - 110, // 54: gotocompany.compass.v1beta1.Asset.created_at:type_name -> google.protobuf.Timestamp - 110, // 55: gotocompany.compass.v1beta1.Asset.updated_at:type_name -> google.protobuf.Timestamp - 86, // 56: gotocompany.compass.v1beta1.Asset.probes:type_name -> gotocompany.compass.v1beta1.Probe - 112, // 57: gotocompany.compass.v1beta1.Probe.metadata:type_name -> google.protobuf.Struct - 110, // 58: gotocompany.compass.v1beta1.Probe.timestamp:type_name -> google.protobuf.Timestamp - 110, // 59: gotocompany.compass.v1beta1.Probe.created_at:type_name -> google.protobuf.Timestamp - 83, // 60: gotocompany.compass.v1beta1.Discussion.owner:type_name -> gotocompany.compass.v1beta1.User - 110, // 61: gotocompany.compass.v1beta1.Discussion.created_at:type_name -> google.protobuf.Timestamp - 110, // 62: gotocompany.compass.v1beta1.Discussion.updated_at:type_name -> google.protobuf.Timestamp - 83, // 63: gotocompany.compass.v1beta1.Comment.owner:type_name -> gotocompany.compass.v1beta1.User - 83, // 64: gotocompany.compass.v1beta1.Comment.updated_by:type_name -> gotocompany.compass.v1beta1.User - 110, // 65: gotocompany.compass.v1beta1.Comment.created_at:type_name -> google.protobuf.Timestamp - 110, // 66: gotocompany.compass.v1beta1.Comment.updated_at:type_name -> google.protobuf.Timestamp - 112, // 67: gotocompany.compass.v1beta1.LineageEdge.prop:type_name -> google.protobuf.Struct - 92, // 68: gotocompany.compass.v1beta1.Tag.tag_values:type_name -> gotocompany.compass.v1beta1.TagValue - 111, // 69: gotocompany.compass.v1beta1.TagValue.field_value:type_name -> google.protobuf.Value - 110, // 70: gotocompany.compass.v1beta1.TagValue.created_at:type_name -> google.protobuf.Timestamp - 110, // 71: gotocompany.compass.v1beta1.TagValue.updated_at:type_name -> google.protobuf.Timestamp - 94, // 72: gotocompany.compass.v1beta1.TagTemplate.fields:type_name -> gotocompany.compass.v1beta1.TagTemplateField - 110, // 73: gotocompany.compass.v1beta1.TagTemplate.created_at:type_name -> google.protobuf.Timestamp - 110, // 74: gotocompany.compass.v1beta1.TagTemplate.updated_at:type_name -> google.protobuf.Timestamp - 110, // 75: gotocompany.compass.v1beta1.TagTemplateField.created_at:type_name -> google.protobuf.Timestamp - 110, // 76: gotocompany.compass.v1beta1.TagTemplateField.updated_at:type_name -> google.protobuf.Timestamp - 86, // 77: gotocompany.compass.v1beta1.GetGraphResponse.ProbesInfo.latest:type_name -> gotocompany.compass.v1beta1.Probe - 99, // 78: gotocompany.compass.v1beta1.GetGraphResponse.NodeAttributes.probes:type_name -> gotocompany.compass.v1beta1.GetGraphResponse.ProbesInfo - 100, // 79: gotocompany.compass.v1beta1.GetGraphResponse.NodeAttrsEntry.value:type_name -> gotocompany.compass.v1beta1.GetGraphResponse.NodeAttributes - 112, // 80: gotocompany.compass.v1beta1.UpsertAssetRequest.Asset.data:type_name -> google.protobuf.Struct - 105, // 81: gotocompany.compass.v1beta1.UpsertAssetRequest.Asset.labels:type_name -> gotocompany.compass.v1beta1.UpsertAssetRequest.Asset.LabelsEntry - 83, // 82: gotocompany.compass.v1beta1.UpsertAssetRequest.Asset.owners:type_name -> gotocompany.compass.v1beta1.User - 113, // 83: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.name:type_name -> google.protobuf.StringValue - 113, // 84: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.description:type_name -> google.protobuf.StringValue - 112, // 85: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.data:type_name -> google.protobuf.Struct - 107, // 86: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.labels:type_name -> gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.LabelsEntry - 83, // 87: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.owners:type_name -> gotocompany.compass.v1beta1.User - 112, // 88: gotocompany.compass.v1beta1.CreateAssetProbeRequest.Probe.metadata:type_name -> google.protobuf.Struct - 110, // 89: gotocompany.compass.v1beta1.CreateAssetProbeRequest.Probe.timestamp:type_name -> google.protobuf.Timestamp + 87, // 10: gotocompany.compass.v1beta1.AssetGroup.assets:type_name -> gotocompany.compass.v1beta1.Asset + 87, // 11: gotocompany.compass.v1beta1.SearchAssetsResponse.data:type_name -> gotocompany.compass.v1beta1.Asset + 91, // 12: gotocompany.compass.v1beta1.GetGraphResponse.data:type_name -> gotocompany.compass.v1beta1.LineageEdge + 103, // 13: gotocompany.compass.v1beta1.GetGraphResponse.node_attrs:type_name -> gotocompany.compass.v1beta1.GetGraphResponse.NodeAttrsEntry + 104, // 14: gotocompany.compass.v1beta1.GetAllTypesRequest.data:type_name -> gotocompany.compass.v1beta1.GetAllTypesRequest.DataEntry + 97, // 15: gotocompany.compass.v1beta1.GetAllTypesResponse.data:type_name -> gotocompany.compass.v1beta1.Type + 105, // 16: gotocompany.compass.v1beta1.GetAllAssetsRequest.data:type_name -> gotocompany.compass.v1beta1.GetAllAssetsRequest.DataEntry + 87, // 17: gotocompany.compass.v1beta1.GetAllAssetsResponse.data:type_name -> gotocompany.compass.v1beta1.Asset + 87, // 18: gotocompany.compass.v1beta1.GetAssetByIDResponse.data:type_name -> gotocompany.compass.v1beta1.Asset + 106, // 19: gotocompany.compass.v1beta1.UpsertAssetRequest.asset:type_name -> gotocompany.compass.v1beta1.UpsertAssetRequest.Asset + 92, // 20: gotocompany.compass.v1beta1.UpsertAssetRequest.upstreams:type_name -> gotocompany.compass.v1beta1.LineageNode + 92, // 21: gotocompany.compass.v1beta1.UpsertAssetRequest.downstreams:type_name -> gotocompany.compass.v1beta1.LineageNode + 108, // 22: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.asset:type_name -> gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset + 92, // 23: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.upstreams:type_name -> gotocompany.compass.v1beta1.LineageNode + 92, // 24: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.downstreams:type_name -> gotocompany.compass.v1beta1.LineageNode + 85, // 25: gotocompany.compass.v1beta1.GetAssetStargazersResponse.data:type_name -> gotocompany.compass.v1beta1.User + 87, // 26: gotocompany.compass.v1beta1.GetAssetVersionHistoryResponse.data:type_name -> gotocompany.compass.v1beta1.Asset + 87, // 27: gotocompany.compass.v1beta1.GetAssetByVersionResponse.data:type_name -> gotocompany.compass.v1beta1.Asset + 110, // 28: gotocompany.compass.v1beta1.CreateAssetProbeRequest.probe:type_name -> gotocompany.compass.v1beta1.CreateAssetProbeRequest.Probe + 87, // 29: gotocompany.compass.v1beta1.GetUserStarredAssetsResponse.data:type_name -> gotocompany.compass.v1beta1.Asset + 87, // 30: gotocompany.compass.v1beta1.GetMyStarredAssetsResponse.data:type_name -> gotocompany.compass.v1beta1.Asset + 87, // 31: gotocompany.compass.v1beta1.GetMyStarredAssetResponse.data:type_name -> gotocompany.compass.v1beta1.Asset + 89, // 32: gotocompany.compass.v1beta1.GetMyDiscussionsResponse.data:type_name -> gotocompany.compass.v1beta1.Discussion + 94, // 33: gotocompany.compass.v1beta1.CreateTagAssetRequest.tag_values:type_name -> gotocompany.compass.v1beta1.TagValue + 93, // 34: gotocompany.compass.v1beta1.CreateTagAssetResponse.data:type_name -> gotocompany.compass.v1beta1.Tag + 93, // 35: gotocompany.compass.v1beta1.GetTagByAssetAndTemplateResponse.data:type_name -> gotocompany.compass.v1beta1.Tag + 94, // 36: gotocompany.compass.v1beta1.UpdateTagAssetRequest.tag_values:type_name -> gotocompany.compass.v1beta1.TagValue + 93, // 37: gotocompany.compass.v1beta1.UpdateTagAssetResponse.data:type_name -> gotocompany.compass.v1beta1.Tag + 93, // 38: gotocompany.compass.v1beta1.GetAllTagsByAssetResponse.data:type_name -> gotocompany.compass.v1beta1.Tag + 95, // 39: gotocompany.compass.v1beta1.GetAllTagTemplatesResponse.data:type_name -> gotocompany.compass.v1beta1.TagTemplate + 96, // 40: gotocompany.compass.v1beta1.CreateTagTemplateRequest.fields:type_name -> gotocompany.compass.v1beta1.TagTemplateField + 95, // 41: gotocompany.compass.v1beta1.CreateTagTemplateResponse.data:type_name -> gotocompany.compass.v1beta1.TagTemplate + 95, // 42: gotocompany.compass.v1beta1.GetTagTemplateResponse.data:type_name -> gotocompany.compass.v1beta1.TagTemplate + 96, // 43: gotocompany.compass.v1beta1.UpdateTagTemplateRequest.fields:type_name -> gotocompany.compass.v1beta1.TagTemplateField + 95, // 44: gotocompany.compass.v1beta1.UpdateTagTemplateResponse.data:type_name -> gotocompany.compass.v1beta1.TagTemplate + 112, // 45: gotocompany.compass.v1beta1.User.created_at:type_name -> google.protobuf.Timestamp + 112, // 46: gotocompany.compass.v1beta1.User.updated_at:type_name -> google.protobuf.Timestamp + 113, // 47: gotocompany.compass.v1beta1.Change.from:type_name -> google.protobuf.Value + 113, // 48: gotocompany.compass.v1beta1.Change.to:type_name -> google.protobuf.Value + 114, // 49: gotocompany.compass.v1beta1.Asset.data:type_name -> google.protobuf.Struct + 111, // 50: gotocompany.compass.v1beta1.Asset.labels:type_name -> gotocompany.compass.v1beta1.Asset.LabelsEntry + 85, // 51: gotocompany.compass.v1beta1.Asset.owners:type_name -> gotocompany.compass.v1beta1.User + 85, // 52: gotocompany.compass.v1beta1.Asset.updated_by:type_name -> gotocompany.compass.v1beta1.User + 86, // 53: gotocompany.compass.v1beta1.Asset.changelog:type_name -> gotocompany.compass.v1beta1.Change + 112, // 54: gotocompany.compass.v1beta1.Asset.created_at:type_name -> google.protobuf.Timestamp + 112, // 55: gotocompany.compass.v1beta1.Asset.updated_at:type_name -> google.protobuf.Timestamp + 88, // 56: gotocompany.compass.v1beta1.Asset.probes:type_name -> gotocompany.compass.v1beta1.Probe + 114, // 57: gotocompany.compass.v1beta1.Probe.metadata:type_name -> google.protobuf.Struct + 112, // 58: gotocompany.compass.v1beta1.Probe.timestamp:type_name -> google.protobuf.Timestamp + 112, // 59: gotocompany.compass.v1beta1.Probe.created_at:type_name -> google.protobuf.Timestamp + 85, // 60: gotocompany.compass.v1beta1.Discussion.owner:type_name -> gotocompany.compass.v1beta1.User + 112, // 61: gotocompany.compass.v1beta1.Discussion.created_at:type_name -> google.protobuf.Timestamp + 112, // 62: gotocompany.compass.v1beta1.Discussion.updated_at:type_name -> google.protobuf.Timestamp + 85, // 63: gotocompany.compass.v1beta1.Comment.owner:type_name -> gotocompany.compass.v1beta1.User + 85, // 64: gotocompany.compass.v1beta1.Comment.updated_by:type_name -> gotocompany.compass.v1beta1.User + 112, // 65: gotocompany.compass.v1beta1.Comment.created_at:type_name -> google.protobuf.Timestamp + 112, // 66: gotocompany.compass.v1beta1.Comment.updated_at:type_name -> google.protobuf.Timestamp + 114, // 67: gotocompany.compass.v1beta1.LineageEdge.prop:type_name -> google.protobuf.Struct + 94, // 68: gotocompany.compass.v1beta1.Tag.tag_values:type_name -> gotocompany.compass.v1beta1.TagValue + 113, // 69: gotocompany.compass.v1beta1.TagValue.field_value:type_name -> google.protobuf.Value + 112, // 70: gotocompany.compass.v1beta1.TagValue.created_at:type_name -> google.protobuf.Timestamp + 112, // 71: gotocompany.compass.v1beta1.TagValue.updated_at:type_name -> google.protobuf.Timestamp + 96, // 72: gotocompany.compass.v1beta1.TagTemplate.fields:type_name -> gotocompany.compass.v1beta1.TagTemplateField + 112, // 73: gotocompany.compass.v1beta1.TagTemplate.created_at:type_name -> google.protobuf.Timestamp + 112, // 74: gotocompany.compass.v1beta1.TagTemplate.updated_at:type_name -> google.protobuf.Timestamp + 112, // 75: gotocompany.compass.v1beta1.TagTemplateField.created_at:type_name -> google.protobuf.Timestamp + 112, // 76: gotocompany.compass.v1beta1.TagTemplateField.updated_at:type_name -> google.protobuf.Timestamp + 88, // 77: gotocompany.compass.v1beta1.GetGraphResponse.ProbesInfo.latest:type_name -> gotocompany.compass.v1beta1.Probe + 101, // 78: gotocompany.compass.v1beta1.GetGraphResponse.NodeAttributes.probes:type_name -> gotocompany.compass.v1beta1.GetGraphResponse.ProbesInfo + 102, // 79: gotocompany.compass.v1beta1.GetGraphResponse.NodeAttrsEntry.value:type_name -> gotocompany.compass.v1beta1.GetGraphResponse.NodeAttributes + 114, // 80: gotocompany.compass.v1beta1.UpsertAssetRequest.Asset.data:type_name -> google.protobuf.Struct + 107, // 81: gotocompany.compass.v1beta1.UpsertAssetRequest.Asset.labels:type_name -> gotocompany.compass.v1beta1.UpsertAssetRequest.Asset.LabelsEntry + 85, // 82: gotocompany.compass.v1beta1.UpsertAssetRequest.Asset.owners:type_name -> gotocompany.compass.v1beta1.User + 115, // 83: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.name:type_name -> google.protobuf.StringValue + 115, // 84: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.description:type_name -> google.protobuf.StringValue + 114, // 85: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.data:type_name -> google.protobuf.Struct + 109, // 86: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.labels:type_name -> gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.LabelsEntry + 85, // 87: gotocompany.compass.v1beta1.UpsertPatchAssetRequest.Asset.owners:type_name -> gotocompany.compass.v1beta1.User + 114, // 88: gotocompany.compass.v1beta1.CreateAssetProbeRequest.Probe.metadata:type_name -> google.protobuf.Struct + 112, // 89: gotocompany.compass.v1beta1.CreateAssetProbeRequest.Probe.timestamp:type_name -> google.protobuf.Timestamp 0, // 90: gotocompany.compass.v1beta1.CompassService.GetAllDiscussions:input_type -> gotocompany.compass.v1beta1.GetAllDiscussionsRequest 2, // 91: gotocompany.compass.v1beta1.CompassService.CreateDiscussion:input_type -> gotocompany.compass.v1beta1.CreateDiscussionRequest 4, // 92: gotocompany.compass.v1beta1.CompassService.GetDiscussion:input_type -> gotocompany.compass.v1beta1.GetDiscussionRequest @@ -8383,69 +8525,71 @@ var file_gotocompany_compass_v1beta1_service_proto_depIdxs = []int32{ 35, // 106: gotocompany.compass.v1beta1.CompassService.UpsertAsset:input_type -> gotocompany.compass.v1beta1.UpsertAssetRequest 37, // 107: gotocompany.compass.v1beta1.CompassService.UpsertPatchAsset:input_type -> gotocompany.compass.v1beta1.UpsertPatchAssetRequest 39, // 108: gotocompany.compass.v1beta1.CompassService.DeleteAsset:input_type -> gotocompany.compass.v1beta1.DeleteAssetRequest - 41, // 109: gotocompany.compass.v1beta1.CompassService.GetAssetStargazers:input_type -> gotocompany.compass.v1beta1.GetAssetStargazersRequest - 43, // 110: gotocompany.compass.v1beta1.CompassService.GetAssetVersionHistory:input_type -> gotocompany.compass.v1beta1.GetAssetVersionHistoryRequest - 45, // 111: gotocompany.compass.v1beta1.CompassService.GetAssetByVersion:input_type -> gotocompany.compass.v1beta1.GetAssetByVersionRequest - 47, // 112: gotocompany.compass.v1beta1.CompassService.CreateAssetProbe:input_type -> gotocompany.compass.v1beta1.CreateAssetProbeRequest - 49, // 113: gotocompany.compass.v1beta1.CompassService.SyncAssets:input_type -> gotocompany.compass.v1beta1.SyncAssetsRequest - 51, // 114: gotocompany.compass.v1beta1.CompassService.GetUserStarredAssets:input_type -> gotocompany.compass.v1beta1.GetUserStarredAssetsRequest - 53, // 115: gotocompany.compass.v1beta1.CompassService.GetMyStarredAssets:input_type -> gotocompany.compass.v1beta1.GetMyStarredAssetsRequest - 55, // 116: gotocompany.compass.v1beta1.CompassService.GetMyStarredAsset:input_type -> gotocompany.compass.v1beta1.GetMyStarredAssetRequest - 57, // 117: gotocompany.compass.v1beta1.CompassService.StarAsset:input_type -> gotocompany.compass.v1beta1.StarAssetRequest - 59, // 118: gotocompany.compass.v1beta1.CompassService.UnstarAsset:input_type -> gotocompany.compass.v1beta1.UnstarAssetRequest - 61, // 119: gotocompany.compass.v1beta1.CompassService.GetMyDiscussions:input_type -> gotocompany.compass.v1beta1.GetMyDiscussionsRequest - 63, // 120: gotocompany.compass.v1beta1.CompassService.CreateTagAsset:input_type -> gotocompany.compass.v1beta1.CreateTagAssetRequest - 65, // 121: gotocompany.compass.v1beta1.CompassService.GetTagByAssetAndTemplate:input_type -> gotocompany.compass.v1beta1.GetTagByAssetAndTemplateRequest - 67, // 122: gotocompany.compass.v1beta1.CompassService.UpdateTagAsset:input_type -> gotocompany.compass.v1beta1.UpdateTagAssetRequest - 69, // 123: gotocompany.compass.v1beta1.CompassService.DeleteTagAsset:input_type -> gotocompany.compass.v1beta1.DeleteTagAssetRequest - 71, // 124: gotocompany.compass.v1beta1.CompassService.GetAllTagsByAsset:input_type -> gotocompany.compass.v1beta1.GetAllTagsByAssetRequest - 73, // 125: gotocompany.compass.v1beta1.CompassService.GetAllTagTemplates:input_type -> gotocompany.compass.v1beta1.GetAllTagTemplatesRequest - 75, // 126: gotocompany.compass.v1beta1.CompassService.CreateTagTemplate:input_type -> gotocompany.compass.v1beta1.CreateTagTemplateRequest - 77, // 127: gotocompany.compass.v1beta1.CompassService.GetTagTemplate:input_type -> gotocompany.compass.v1beta1.GetTagTemplateRequest - 79, // 128: gotocompany.compass.v1beta1.CompassService.UpdateTagTemplate:input_type -> gotocompany.compass.v1beta1.UpdateTagTemplateRequest - 81, // 129: gotocompany.compass.v1beta1.CompassService.DeleteTagTemplate:input_type -> gotocompany.compass.v1beta1.DeleteTagTemplateRequest - 1, // 130: gotocompany.compass.v1beta1.CompassService.GetAllDiscussions:output_type -> gotocompany.compass.v1beta1.GetAllDiscussionsResponse - 3, // 131: gotocompany.compass.v1beta1.CompassService.CreateDiscussion:output_type -> gotocompany.compass.v1beta1.CreateDiscussionResponse - 5, // 132: gotocompany.compass.v1beta1.CompassService.GetDiscussion:output_type -> gotocompany.compass.v1beta1.GetDiscussionResponse - 8, // 133: gotocompany.compass.v1beta1.CompassService.PatchDiscussion:output_type -> gotocompany.compass.v1beta1.PatchDiscussionResponse - 9, // 134: gotocompany.compass.v1beta1.CompassService.CreateComment:output_type -> gotocompany.compass.v1beta1.CreateCommentResponse - 11, // 135: gotocompany.compass.v1beta1.CompassService.GetAllComments:output_type -> gotocompany.compass.v1beta1.GetAllCommentsResponse - 13, // 136: gotocompany.compass.v1beta1.CompassService.GetComment:output_type -> gotocompany.compass.v1beta1.GetCommentResponse - 15, // 137: gotocompany.compass.v1beta1.CompassService.UpdateComment:output_type -> gotocompany.compass.v1beta1.UpdateCommentResponse - 17, // 138: gotocompany.compass.v1beta1.CompassService.DeleteComment:output_type -> gotocompany.compass.v1beta1.DeleteCommentResponse - 24, // 139: gotocompany.compass.v1beta1.CompassService.SearchAssets:output_type -> gotocompany.compass.v1beta1.SearchAssetsResponse - 20, // 140: gotocompany.compass.v1beta1.CompassService.GroupAssets:output_type -> gotocompany.compass.v1beta1.GroupAssetsResponse - 26, // 141: gotocompany.compass.v1beta1.CompassService.SuggestAssets:output_type -> gotocompany.compass.v1beta1.SuggestAssetsResponse - 28, // 142: gotocompany.compass.v1beta1.CompassService.GetGraph:output_type -> gotocompany.compass.v1beta1.GetGraphResponse - 30, // 143: gotocompany.compass.v1beta1.CompassService.GetAllTypes:output_type -> gotocompany.compass.v1beta1.GetAllTypesResponse - 32, // 144: gotocompany.compass.v1beta1.CompassService.GetAllAssets:output_type -> gotocompany.compass.v1beta1.GetAllAssetsResponse - 34, // 145: gotocompany.compass.v1beta1.CompassService.GetAssetByID:output_type -> gotocompany.compass.v1beta1.GetAssetByIDResponse - 36, // 146: gotocompany.compass.v1beta1.CompassService.UpsertAsset:output_type -> gotocompany.compass.v1beta1.UpsertAssetResponse - 38, // 147: gotocompany.compass.v1beta1.CompassService.UpsertPatchAsset:output_type -> gotocompany.compass.v1beta1.UpsertPatchAssetResponse - 40, // 148: gotocompany.compass.v1beta1.CompassService.DeleteAsset:output_type -> gotocompany.compass.v1beta1.DeleteAssetResponse - 42, // 149: gotocompany.compass.v1beta1.CompassService.GetAssetStargazers:output_type -> gotocompany.compass.v1beta1.GetAssetStargazersResponse - 44, // 150: gotocompany.compass.v1beta1.CompassService.GetAssetVersionHistory:output_type -> gotocompany.compass.v1beta1.GetAssetVersionHistoryResponse - 46, // 151: gotocompany.compass.v1beta1.CompassService.GetAssetByVersion:output_type -> gotocompany.compass.v1beta1.GetAssetByVersionResponse - 48, // 152: gotocompany.compass.v1beta1.CompassService.CreateAssetProbe:output_type -> gotocompany.compass.v1beta1.CreateAssetProbeResponse - 50, // 153: gotocompany.compass.v1beta1.CompassService.SyncAssets:output_type -> gotocompany.compass.v1beta1.SyncAssetsResponse - 52, // 154: gotocompany.compass.v1beta1.CompassService.GetUserStarredAssets:output_type -> gotocompany.compass.v1beta1.GetUserStarredAssetsResponse - 54, // 155: gotocompany.compass.v1beta1.CompassService.GetMyStarredAssets:output_type -> gotocompany.compass.v1beta1.GetMyStarredAssetsResponse - 56, // 156: gotocompany.compass.v1beta1.CompassService.GetMyStarredAsset:output_type -> gotocompany.compass.v1beta1.GetMyStarredAssetResponse - 58, // 157: gotocompany.compass.v1beta1.CompassService.StarAsset:output_type -> gotocompany.compass.v1beta1.StarAssetResponse - 60, // 158: gotocompany.compass.v1beta1.CompassService.UnstarAsset:output_type -> gotocompany.compass.v1beta1.UnstarAssetResponse - 62, // 159: gotocompany.compass.v1beta1.CompassService.GetMyDiscussions:output_type -> gotocompany.compass.v1beta1.GetMyDiscussionsResponse - 64, // 160: gotocompany.compass.v1beta1.CompassService.CreateTagAsset:output_type -> gotocompany.compass.v1beta1.CreateTagAssetResponse - 66, // 161: gotocompany.compass.v1beta1.CompassService.GetTagByAssetAndTemplate:output_type -> gotocompany.compass.v1beta1.GetTagByAssetAndTemplateResponse - 68, // 162: gotocompany.compass.v1beta1.CompassService.UpdateTagAsset:output_type -> gotocompany.compass.v1beta1.UpdateTagAssetResponse - 70, // 163: gotocompany.compass.v1beta1.CompassService.DeleteTagAsset:output_type -> gotocompany.compass.v1beta1.DeleteTagAssetResponse - 72, // 164: gotocompany.compass.v1beta1.CompassService.GetAllTagsByAsset:output_type -> gotocompany.compass.v1beta1.GetAllTagsByAssetResponse - 74, // 165: gotocompany.compass.v1beta1.CompassService.GetAllTagTemplates:output_type -> gotocompany.compass.v1beta1.GetAllTagTemplatesResponse - 76, // 166: gotocompany.compass.v1beta1.CompassService.CreateTagTemplate:output_type -> gotocompany.compass.v1beta1.CreateTagTemplateResponse - 78, // 167: gotocompany.compass.v1beta1.CompassService.GetTagTemplate:output_type -> gotocompany.compass.v1beta1.GetTagTemplateResponse - 80, // 168: gotocompany.compass.v1beta1.CompassService.UpdateTagTemplate:output_type -> gotocompany.compass.v1beta1.UpdateTagTemplateResponse - 82, // 169: gotocompany.compass.v1beta1.CompassService.DeleteTagTemplate:output_type -> gotocompany.compass.v1beta1.DeleteTagTemplateResponse - 130, // [130:170] is the sub-list for method output_type - 90, // [90:130] is the sub-list for method input_type + 41, // 109: gotocompany.compass.v1beta1.CompassService.DeleteAssets:input_type -> gotocompany.compass.v1beta1.DeleteAssetsRequest + 43, // 110: gotocompany.compass.v1beta1.CompassService.GetAssetStargazers:input_type -> gotocompany.compass.v1beta1.GetAssetStargazersRequest + 45, // 111: gotocompany.compass.v1beta1.CompassService.GetAssetVersionHistory:input_type -> gotocompany.compass.v1beta1.GetAssetVersionHistoryRequest + 47, // 112: gotocompany.compass.v1beta1.CompassService.GetAssetByVersion:input_type -> gotocompany.compass.v1beta1.GetAssetByVersionRequest + 49, // 113: gotocompany.compass.v1beta1.CompassService.CreateAssetProbe:input_type -> gotocompany.compass.v1beta1.CreateAssetProbeRequest + 51, // 114: gotocompany.compass.v1beta1.CompassService.SyncAssets:input_type -> gotocompany.compass.v1beta1.SyncAssetsRequest + 53, // 115: gotocompany.compass.v1beta1.CompassService.GetUserStarredAssets:input_type -> gotocompany.compass.v1beta1.GetUserStarredAssetsRequest + 55, // 116: gotocompany.compass.v1beta1.CompassService.GetMyStarredAssets:input_type -> gotocompany.compass.v1beta1.GetMyStarredAssetsRequest + 57, // 117: gotocompany.compass.v1beta1.CompassService.GetMyStarredAsset:input_type -> gotocompany.compass.v1beta1.GetMyStarredAssetRequest + 59, // 118: gotocompany.compass.v1beta1.CompassService.StarAsset:input_type -> gotocompany.compass.v1beta1.StarAssetRequest + 61, // 119: gotocompany.compass.v1beta1.CompassService.UnstarAsset:input_type -> gotocompany.compass.v1beta1.UnstarAssetRequest + 63, // 120: gotocompany.compass.v1beta1.CompassService.GetMyDiscussions:input_type -> gotocompany.compass.v1beta1.GetMyDiscussionsRequest + 65, // 121: gotocompany.compass.v1beta1.CompassService.CreateTagAsset:input_type -> gotocompany.compass.v1beta1.CreateTagAssetRequest + 67, // 122: gotocompany.compass.v1beta1.CompassService.GetTagByAssetAndTemplate:input_type -> gotocompany.compass.v1beta1.GetTagByAssetAndTemplateRequest + 69, // 123: gotocompany.compass.v1beta1.CompassService.UpdateTagAsset:input_type -> gotocompany.compass.v1beta1.UpdateTagAssetRequest + 71, // 124: gotocompany.compass.v1beta1.CompassService.DeleteTagAsset:input_type -> gotocompany.compass.v1beta1.DeleteTagAssetRequest + 73, // 125: gotocompany.compass.v1beta1.CompassService.GetAllTagsByAsset:input_type -> gotocompany.compass.v1beta1.GetAllTagsByAssetRequest + 75, // 126: gotocompany.compass.v1beta1.CompassService.GetAllTagTemplates:input_type -> gotocompany.compass.v1beta1.GetAllTagTemplatesRequest + 77, // 127: gotocompany.compass.v1beta1.CompassService.CreateTagTemplate:input_type -> gotocompany.compass.v1beta1.CreateTagTemplateRequest + 79, // 128: gotocompany.compass.v1beta1.CompassService.GetTagTemplate:input_type -> gotocompany.compass.v1beta1.GetTagTemplateRequest + 81, // 129: gotocompany.compass.v1beta1.CompassService.UpdateTagTemplate:input_type -> gotocompany.compass.v1beta1.UpdateTagTemplateRequest + 83, // 130: gotocompany.compass.v1beta1.CompassService.DeleteTagTemplate:input_type -> gotocompany.compass.v1beta1.DeleteTagTemplateRequest + 1, // 131: gotocompany.compass.v1beta1.CompassService.GetAllDiscussions:output_type -> gotocompany.compass.v1beta1.GetAllDiscussionsResponse + 3, // 132: gotocompany.compass.v1beta1.CompassService.CreateDiscussion:output_type -> gotocompany.compass.v1beta1.CreateDiscussionResponse + 5, // 133: gotocompany.compass.v1beta1.CompassService.GetDiscussion:output_type -> gotocompany.compass.v1beta1.GetDiscussionResponse + 8, // 134: gotocompany.compass.v1beta1.CompassService.PatchDiscussion:output_type -> gotocompany.compass.v1beta1.PatchDiscussionResponse + 9, // 135: gotocompany.compass.v1beta1.CompassService.CreateComment:output_type -> gotocompany.compass.v1beta1.CreateCommentResponse + 11, // 136: gotocompany.compass.v1beta1.CompassService.GetAllComments:output_type -> gotocompany.compass.v1beta1.GetAllCommentsResponse + 13, // 137: gotocompany.compass.v1beta1.CompassService.GetComment:output_type -> gotocompany.compass.v1beta1.GetCommentResponse + 15, // 138: gotocompany.compass.v1beta1.CompassService.UpdateComment:output_type -> gotocompany.compass.v1beta1.UpdateCommentResponse + 17, // 139: gotocompany.compass.v1beta1.CompassService.DeleteComment:output_type -> gotocompany.compass.v1beta1.DeleteCommentResponse + 24, // 140: gotocompany.compass.v1beta1.CompassService.SearchAssets:output_type -> gotocompany.compass.v1beta1.SearchAssetsResponse + 20, // 141: gotocompany.compass.v1beta1.CompassService.GroupAssets:output_type -> gotocompany.compass.v1beta1.GroupAssetsResponse + 26, // 142: gotocompany.compass.v1beta1.CompassService.SuggestAssets:output_type -> gotocompany.compass.v1beta1.SuggestAssetsResponse + 28, // 143: gotocompany.compass.v1beta1.CompassService.GetGraph:output_type -> gotocompany.compass.v1beta1.GetGraphResponse + 30, // 144: gotocompany.compass.v1beta1.CompassService.GetAllTypes:output_type -> gotocompany.compass.v1beta1.GetAllTypesResponse + 32, // 145: gotocompany.compass.v1beta1.CompassService.GetAllAssets:output_type -> gotocompany.compass.v1beta1.GetAllAssetsResponse + 34, // 146: gotocompany.compass.v1beta1.CompassService.GetAssetByID:output_type -> gotocompany.compass.v1beta1.GetAssetByIDResponse + 36, // 147: gotocompany.compass.v1beta1.CompassService.UpsertAsset:output_type -> gotocompany.compass.v1beta1.UpsertAssetResponse + 38, // 148: gotocompany.compass.v1beta1.CompassService.UpsertPatchAsset:output_type -> gotocompany.compass.v1beta1.UpsertPatchAssetResponse + 40, // 149: gotocompany.compass.v1beta1.CompassService.DeleteAsset:output_type -> gotocompany.compass.v1beta1.DeleteAssetResponse + 42, // 150: gotocompany.compass.v1beta1.CompassService.DeleteAssets:output_type -> gotocompany.compass.v1beta1.DeleteAssetsResponse + 44, // 151: gotocompany.compass.v1beta1.CompassService.GetAssetStargazers:output_type -> gotocompany.compass.v1beta1.GetAssetStargazersResponse + 46, // 152: gotocompany.compass.v1beta1.CompassService.GetAssetVersionHistory:output_type -> gotocompany.compass.v1beta1.GetAssetVersionHistoryResponse + 48, // 153: gotocompany.compass.v1beta1.CompassService.GetAssetByVersion:output_type -> gotocompany.compass.v1beta1.GetAssetByVersionResponse + 50, // 154: gotocompany.compass.v1beta1.CompassService.CreateAssetProbe:output_type -> gotocompany.compass.v1beta1.CreateAssetProbeResponse + 52, // 155: gotocompany.compass.v1beta1.CompassService.SyncAssets:output_type -> gotocompany.compass.v1beta1.SyncAssetsResponse + 54, // 156: gotocompany.compass.v1beta1.CompassService.GetUserStarredAssets:output_type -> gotocompany.compass.v1beta1.GetUserStarredAssetsResponse + 56, // 157: gotocompany.compass.v1beta1.CompassService.GetMyStarredAssets:output_type -> gotocompany.compass.v1beta1.GetMyStarredAssetsResponse + 58, // 158: gotocompany.compass.v1beta1.CompassService.GetMyStarredAsset:output_type -> gotocompany.compass.v1beta1.GetMyStarredAssetResponse + 60, // 159: gotocompany.compass.v1beta1.CompassService.StarAsset:output_type -> gotocompany.compass.v1beta1.StarAssetResponse + 62, // 160: gotocompany.compass.v1beta1.CompassService.UnstarAsset:output_type -> gotocompany.compass.v1beta1.UnstarAssetResponse + 64, // 161: gotocompany.compass.v1beta1.CompassService.GetMyDiscussions:output_type -> gotocompany.compass.v1beta1.GetMyDiscussionsResponse + 66, // 162: gotocompany.compass.v1beta1.CompassService.CreateTagAsset:output_type -> gotocompany.compass.v1beta1.CreateTagAssetResponse + 68, // 163: gotocompany.compass.v1beta1.CompassService.GetTagByAssetAndTemplate:output_type -> gotocompany.compass.v1beta1.GetTagByAssetAndTemplateResponse + 70, // 164: gotocompany.compass.v1beta1.CompassService.UpdateTagAsset:output_type -> gotocompany.compass.v1beta1.UpdateTagAssetResponse + 72, // 165: gotocompany.compass.v1beta1.CompassService.DeleteTagAsset:output_type -> gotocompany.compass.v1beta1.DeleteTagAssetResponse + 74, // 166: gotocompany.compass.v1beta1.CompassService.GetAllTagsByAsset:output_type -> gotocompany.compass.v1beta1.GetAllTagsByAssetResponse + 76, // 167: gotocompany.compass.v1beta1.CompassService.GetAllTagTemplates:output_type -> gotocompany.compass.v1beta1.GetAllTagTemplatesResponse + 78, // 168: gotocompany.compass.v1beta1.CompassService.CreateTagTemplate:output_type -> gotocompany.compass.v1beta1.CreateTagTemplateResponse + 80, // 169: gotocompany.compass.v1beta1.CompassService.GetTagTemplate:output_type -> gotocompany.compass.v1beta1.GetTagTemplateResponse + 82, // 170: gotocompany.compass.v1beta1.CompassService.UpdateTagTemplate:output_type -> gotocompany.compass.v1beta1.UpdateTagTemplateResponse + 84, // 171: gotocompany.compass.v1beta1.CompassService.DeleteTagTemplate:output_type -> gotocompany.compass.v1beta1.DeleteTagTemplateResponse + 131, // [131:172] is the sub-list for method output_type + 90, // [90:131] is the sub-list for method input_type 90, // [90:90] is the sub-list for extension type_name 90, // [90:90] is the sub-list for extension extendee 0, // [0:90] is the sub-list for field type_name @@ -8950,7 +9094,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAssetStargazersRequest); i { + switch v := v.(*DeleteAssetsRequest); i { case 0: return &v.state case 1: @@ -8962,7 +9106,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAssetStargazersResponse); i { + switch v := v.(*DeleteAssetsResponse); i { case 0: return &v.state case 1: @@ -8974,7 +9118,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAssetVersionHistoryRequest); i { + switch v := v.(*GetAssetStargazersRequest); i { case 0: return &v.state case 1: @@ -8986,7 +9130,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAssetVersionHistoryResponse); i { + switch v := v.(*GetAssetStargazersResponse); i { case 0: return &v.state case 1: @@ -8998,7 +9142,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAssetByVersionRequest); i { + switch v := v.(*GetAssetVersionHistoryRequest); i { case 0: return &v.state case 1: @@ -9010,7 +9154,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAssetByVersionResponse); i { + switch v := v.(*GetAssetVersionHistoryResponse); i { case 0: return &v.state case 1: @@ -9022,7 +9166,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateAssetProbeRequest); i { + switch v := v.(*GetAssetByVersionRequest); i { case 0: return &v.state case 1: @@ -9034,7 +9178,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateAssetProbeResponse); i { + switch v := v.(*GetAssetByVersionResponse); i { case 0: return &v.state case 1: @@ -9046,7 +9190,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SyncAssetsRequest); i { + switch v := v.(*CreateAssetProbeRequest); i { case 0: return &v.state case 1: @@ -9058,7 +9202,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SyncAssetsResponse); i { + switch v := v.(*CreateAssetProbeResponse); i { case 0: return &v.state case 1: @@ -9070,7 +9214,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUserStarredAssetsRequest); i { + switch v := v.(*SyncAssetsRequest); i { case 0: return &v.state case 1: @@ -9082,7 +9226,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetUserStarredAssetsResponse); i { + switch v := v.(*SyncAssetsResponse); i { case 0: return &v.state case 1: @@ -9094,7 +9238,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMyStarredAssetsRequest); i { + switch v := v.(*GetUserStarredAssetsRequest); i { case 0: return &v.state case 1: @@ -9106,7 +9250,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMyStarredAssetsResponse); i { + switch v := v.(*GetUserStarredAssetsResponse); i { case 0: return &v.state case 1: @@ -9118,7 +9262,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMyStarredAssetRequest); i { + switch v := v.(*GetMyStarredAssetsRequest); i { case 0: return &v.state case 1: @@ -9130,7 +9274,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMyStarredAssetResponse); i { + switch v := v.(*GetMyStarredAssetsResponse); i { case 0: return &v.state case 1: @@ -9142,7 +9286,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StarAssetRequest); i { + switch v := v.(*GetMyStarredAssetRequest); i { case 0: return &v.state case 1: @@ -9154,7 +9298,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StarAssetResponse); i { + switch v := v.(*GetMyStarredAssetResponse); i { case 0: return &v.state case 1: @@ -9166,7 +9310,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UnstarAssetRequest); i { + switch v := v.(*StarAssetRequest); i { case 0: return &v.state case 1: @@ -9178,7 +9322,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UnstarAssetResponse); i { + switch v := v.(*StarAssetResponse); i { case 0: return &v.state case 1: @@ -9190,7 +9334,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMyDiscussionsRequest); i { + switch v := v.(*UnstarAssetRequest); i { case 0: return &v.state case 1: @@ -9202,7 +9346,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetMyDiscussionsResponse); i { + switch v := v.(*UnstarAssetResponse); i { case 0: return &v.state case 1: @@ -9214,7 +9358,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateTagAssetRequest); i { + switch v := v.(*GetMyDiscussionsRequest); i { case 0: return &v.state case 1: @@ -9226,7 +9370,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateTagAssetResponse); i { + switch v := v.(*GetMyDiscussionsResponse); i { case 0: return &v.state case 1: @@ -9238,7 +9382,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTagByAssetAndTemplateRequest); i { + switch v := v.(*CreateTagAssetRequest); i { case 0: return &v.state case 1: @@ -9250,7 +9394,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTagByAssetAndTemplateResponse); i { + switch v := v.(*CreateTagAssetResponse); i { case 0: return &v.state case 1: @@ -9262,7 +9406,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateTagAssetRequest); i { + switch v := v.(*GetTagByAssetAndTemplateRequest); i { case 0: return &v.state case 1: @@ -9274,7 +9418,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[68].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateTagAssetResponse); i { + switch v := v.(*GetTagByAssetAndTemplateResponse); i { case 0: return &v.state case 1: @@ -9286,7 +9430,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[69].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteTagAssetRequest); i { + switch v := v.(*UpdateTagAssetRequest); i { case 0: return &v.state case 1: @@ -9298,7 +9442,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[70].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteTagAssetResponse); i { + switch v := v.(*UpdateTagAssetResponse); i { case 0: return &v.state case 1: @@ -9310,7 +9454,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[71].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAllTagsByAssetRequest); i { + switch v := v.(*DeleteTagAssetRequest); i { case 0: return &v.state case 1: @@ -9322,7 +9466,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[72].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAllTagsByAssetResponse); i { + switch v := v.(*DeleteTagAssetResponse); i { case 0: return &v.state case 1: @@ -9334,7 +9478,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[73].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAllTagTemplatesRequest); i { + switch v := v.(*GetAllTagsByAssetRequest); i { case 0: return &v.state case 1: @@ -9346,7 +9490,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[74].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAllTagTemplatesResponse); i { + switch v := v.(*GetAllTagsByAssetResponse); i { case 0: return &v.state case 1: @@ -9358,7 +9502,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[75].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateTagTemplateRequest); i { + switch v := v.(*GetAllTagTemplatesRequest); i { case 0: return &v.state case 1: @@ -9370,7 +9514,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[76].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateTagTemplateResponse); i { + switch v := v.(*GetAllTagTemplatesResponse); i { case 0: return &v.state case 1: @@ -9382,7 +9526,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[77].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTagTemplateRequest); i { + switch v := v.(*CreateTagTemplateRequest); i { case 0: return &v.state case 1: @@ -9394,7 +9538,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[78].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTagTemplateResponse); i { + switch v := v.(*CreateTagTemplateResponse); i { case 0: return &v.state case 1: @@ -9406,7 +9550,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[79].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateTagTemplateRequest); i { + switch v := v.(*GetTagTemplateRequest); i { case 0: return &v.state case 1: @@ -9418,7 +9562,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[80].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*UpdateTagTemplateResponse); i { + switch v := v.(*GetTagTemplateResponse); i { case 0: return &v.state case 1: @@ -9430,7 +9574,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[81].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteTagTemplateRequest); i { + switch v := v.(*UpdateTagTemplateRequest); i { case 0: return &v.state case 1: @@ -9442,7 +9586,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[82].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteTagTemplateResponse); i { + switch v := v.(*UpdateTagTemplateResponse); i { case 0: return &v.state case 1: @@ -9454,7 +9598,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*User); i { + switch v := v.(*DeleteTagTemplateRequest); i { case 0: return &v.state case 1: @@ -9466,7 +9610,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Change); i { + switch v := v.(*DeleteTagTemplateResponse); i { case 0: return &v.state case 1: @@ -9478,7 +9622,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[85].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Asset); i { + switch v := v.(*User); i { case 0: return &v.state case 1: @@ -9490,7 +9634,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[86].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Probe); i { + switch v := v.(*Change); i { case 0: return &v.state case 1: @@ -9502,7 +9646,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[87].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Discussion); i { + switch v := v.(*Asset); i { case 0: return &v.state case 1: @@ -9514,7 +9658,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[88].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Comment); i { + switch v := v.(*Probe); i { case 0: return &v.state case 1: @@ -9526,7 +9670,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[89].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LineageEdge); i { + switch v := v.(*Discussion); i { case 0: return &v.state case 1: @@ -9538,7 +9682,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[90].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LineageNode); i { + switch v := v.(*Comment); i { case 0: return &v.state case 1: @@ -9550,7 +9694,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[91].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Tag); i { + switch v := v.(*LineageEdge); i { case 0: return &v.state case 1: @@ -9562,7 +9706,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[92].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TagValue); i { + switch v := v.(*LineageNode); i { case 0: return &v.state case 1: @@ -9574,7 +9718,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[93].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TagTemplate); i { + switch v := v.(*Tag); i { case 0: return &v.state case 1: @@ -9586,7 +9730,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[94].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TagTemplateField); i { + switch v := v.(*TagValue); i { case 0: return &v.state case 1: @@ -9598,6 +9742,30 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { } } file_gotocompany_compass_v1beta1_service_proto_msgTypes[95].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TagTemplate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gotocompany_compass_v1beta1_service_proto_msgTypes[96].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TagTemplateField); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_gotocompany_compass_v1beta1_service_proto_msgTypes[97].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Type); i { case 0: return &v.state @@ -9609,7 +9777,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { return nil } } - file_gotocompany_compass_v1beta1_service_proto_msgTypes[99].Exporter = func(v interface{}, i int) interface{} { + file_gotocompany_compass_v1beta1_service_proto_msgTypes[101].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetGraphResponse_ProbesInfo); i { case 0: return &v.state @@ -9621,7 +9789,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { return nil } } - file_gotocompany_compass_v1beta1_service_proto_msgTypes[100].Exporter = func(v interface{}, i int) interface{} { + file_gotocompany_compass_v1beta1_service_proto_msgTypes[102].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetGraphResponse_NodeAttributes); i { case 0: return &v.state @@ -9633,7 +9801,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { return nil } } - file_gotocompany_compass_v1beta1_service_proto_msgTypes[104].Exporter = func(v interface{}, i int) interface{} { + file_gotocompany_compass_v1beta1_service_proto_msgTypes[106].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpsertAssetRequest_Asset); i { case 0: return &v.state @@ -9645,7 +9813,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { return nil } } - file_gotocompany_compass_v1beta1_service_proto_msgTypes[106].Exporter = func(v interface{}, i int) interface{} { + file_gotocompany_compass_v1beta1_service_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*UpsertPatchAssetRequest_Asset); i { case 0: return &v.state @@ -9657,7 +9825,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { return nil } } - file_gotocompany_compass_v1beta1_service_proto_msgTypes[108].Exporter = func(v interface{}, i int) interface{} { + file_gotocompany_compass_v1beta1_service_proto_msgTypes[110].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CreateAssetProbeRequest_Probe); i { case 0: return &v.state @@ -9677,7 +9845,7 @@ func file_gotocompany_compass_v1beta1_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_gotocompany_compass_v1beta1_service_proto_rawDesc, NumEnums: 0, - NumMessages: 110, + NumMessages: 112, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/gotocompany/compass/v1beta1/service.pb.gw.go b/proto/gotocompany/compass/v1beta1/service.pb.gw.go index 613e766e..0f8a9650 100644 --- a/proto/gotocompany/compass/v1beta1/service.pb.gw.go +++ b/proto/gotocompany/compass/v1beta1/service.pb.gw.go @@ -1013,6 +1013,40 @@ func local_request_CompassService_DeleteAsset_0(ctx context.Context, marshaler r } +func request_CompassService_DeleteAssets_0(ctx context.Context, marshaler runtime.Marshaler, client CompassServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteAssetsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DeleteAssets(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_CompassService_DeleteAssets_0(ctx context.Context, marshaler runtime.Marshaler, server CompassServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq DeleteAssetsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DeleteAssets(ctx, &protoReq) + return msg, metadata, err + +} + var ( filter_CompassService_GetAssetStargazers_0 = &utilities.DoubleArray{Encoding: map[string]int{"id": 0}, Base: []int{1, 2, 0, 0}, Check: []int{0, 1, 2, 2}} ) @@ -2666,6 +2700,31 @@ func RegisterCompassServiceHandlerServer(ctx context.Context, mux *runtime.Serve }) + mux.Handle("POST", pattern_CompassService_DeleteAssets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/gotocompany.compass.v1beta1.CompassService/DeleteAssets", runtime.WithHTTPPathPattern("/v1beta1/assets/delete-by-query")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_CompassService_DeleteAssets_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_CompassService_DeleteAssets_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_CompassService_GetAssetStargazers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -3650,6 +3709,28 @@ func RegisterCompassServiceHandlerClient(ctx context.Context, mux *runtime.Serve }) + mux.Handle("POST", pattern_CompassService_DeleteAssets_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/gotocompany.compass.v1beta1.CompassService/DeleteAssets", runtime.WithHTTPPathPattern("/v1beta1/assets/delete-by-query")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_CompassService_DeleteAssets_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_CompassService_DeleteAssets_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_CompassService_GetAssetStargazers_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -4154,6 +4235,8 @@ var ( pattern_CompassService_DeleteAsset_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1beta1", "assets", "id"}, "")) + pattern_CompassService_DeleteAssets_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1beta1", "assets", "delete-by-query"}, "")) + pattern_CompassService_GetAssetStargazers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "assets", "id", "stargazers"}, "")) pattern_CompassService_GetAssetVersionHistory_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3}, []string{"v1beta1", "assets", "id", "versions"}, "")) @@ -4236,6 +4319,8 @@ var ( forward_CompassService_DeleteAsset_0 = runtime.ForwardResponseMessage + forward_CompassService_DeleteAssets_0 = runtime.ForwardResponseMessage + forward_CompassService_GetAssetStargazers_0 = runtime.ForwardResponseMessage forward_CompassService_GetAssetVersionHistory_0 = runtime.ForwardResponseMessage diff --git a/proto/gotocompany/compass/v1beta1/service.pb.validate.go b/proto/gotocompany/compass/v1beta1/service.pb.validate.go index 340d566e..67fd742a 100644 --- a/proto/gotocompany/compass/v1beta1/service.pb.validate.go +++ b/proto/gotocompany/compass/v1beta1/service.pb.validate.go @@ -5463,6 +5463,225 @@ var _ interface { ErrorName() string } = DeleteAssetResponseValidationError{} +// Validate checks the field values on DeleteAssetsRequest with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *DeleteAssetsRequest) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on DeleteAssetsRequest with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// DeleteAssetsRequestMultiError, or nil if none found. +func (m *DeleteAssetsRequest) ValidateAll() error { + return m.validate(true) +} + +func (m *DeleteAssetsRequest) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + if utf8.RuneCountInString(m.GetQueryExpr()) < 3 { + err := DeleteAssetsRequestValidationError{ + field: "QueryExpr", + reason: "value length must be at least 3 runes", + } + if !all { + return err + } + errors = append(errors, err) + } + + // no validation rules for DryRun + + if len(errors) > 0 { + return DeleteAssetsRequestMultiError(errors) + } + + return nil +} + +// DeleteAssetsRequestMultiError is an error wrapping multiple validation +// errors returned by DeleteAssetsRequest.ValidateAll() if the designated +// constraints aren't met. +type DeleteAssetsRequestMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m DeleteAssetsRequestMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m DeleteAssetsRequestMultiError) AllErrors() []error { return m } + +// DeleteAssetsRequestValidationError is the validation error returned by +// DeleteAssetsRequest.Validate if the designated constraints aren't met. +type DeleteAssetsRequestValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DeleteAssetsRequestValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DeleteAssetsRequestValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DeleteAssetsRequestValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DeleteAssetsRequestValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DeleteAssetsRequestValidationError) ErrorName() string { + return "DeleteAssetsRequestValidationError" +} + +// Error satisfies the builtin error interface +func (e DeleteAssetsRequestValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sDeleteAssetsRequest.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DeleteAssetsRequestValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DeleteAssetsRequestValidationError{} + +// Validate checks the field values on DeleteAssetsResponse with the rules +// defined in the proto definition for this message. If any rules are +// violated, the first error encountered is returned, or nil if there are no violations. +func (m *DeleteAssetsResponse) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on DeleteAssetsResponse with the rules +// defined in the proto definition for this message. If any rules are +// violated, the result is a list of violation errors wrapped in +// DeleteAssetsResponseMultiError, or nil if none found. +func (m *DeleteAssetsResponse) ValidateAll() error { + return m.validate(true) +} + +func (m *DeleteAssetsResponse) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for AffectedRows + + if len(errors) > 0 { + return DeleteAssetsResponseMultiError(errors) + } + + return nil +} + +// DeleteAssetsResponseMultiError is an error wrapping multiple validation +// errors returned by DeleteAssetsResponse.ValidateAll() if the designated +// constraints aren't met. +type DeleteAssetsResponseMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m DeleteAssetsResponseMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m DeleteAssetsResponseMultiError) AllErrors() []error { return m } + +// DeleteAssetsResponseValidationError is the validation error returned by +// DeleteAssetsResponse.Validate if the designated constraints aren't met. +type DeleteAssetsResponseValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e DeleteAssetsResponseValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e DeleteAssetsResponseValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e DeleteAssetsResponseValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e DeleteAssetsResponseValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e DeleteAssetsResponseValidationError) ErrorName() string { + return "DeleteAssetsResponseValidationError" +} + +// Error satisfies the builtin error interface +func (e DeleteAssetsResponseValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sDeleteAssetsResponse.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = DeleteAssetsResponseValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = DeleteAssetsResponseValidationError{} + // Validate checks the field values on GetAssetStargazersRequest with the rules // defined in the proto definition for this message. If any rules are // violated, the first error encountered is returned, or nil if there are no violations. diff --git a/proto/gotocompany/compass/v1beta1/service_grpc.pb.go b/proto/gotocompany/compass/v1beta1/service_grpc.pb.go index d0af75c1..a8bcfef0 100644 --- a/proto/gotocompany/compass/v1beta1/service_grpc.pb.go +++ b/proto/gotocompany/compass/v1beta1/service_grpc.pb.go @@ -38,6 +38,7 @@ const ( CompassService_UpsertAsset_FullMethodName = "/gotocompany.compass.v1beta1.CompassService/UpsertAsset" CompassService_UpsertPatchAsset_FullMethodName = "/gotocompany.compass.v1beta1.CompassService/UpsertPatchAsset" CompassService_DeleteAsset_FullMethodName = "/gotocompany.compass.v1beta1.CompassService/DeleteAsset" + CompassService_DeleteAssets_FullMethodName = "/gotocompany.compass.v1beta1.CompassService/DeleteAssets" CompassService_GetAssetStargazers_FullMethodName = "/gotocompany.compass.v1beta1.CompassService/GetAssetStargazers" CompassService_GetAssetVersionHistory_FullMethodName = "/gotocompany.compass.v1beta1.CompassService/GetAssetVersionHistory" CompassService_GetAssetByVersion_FullMethodName = "/gotocompany.compass.v1beta1.CompassService/GetAssetByVersion" @@ -87,6 +88,7 @@ type CompassServiceClient interface { UpsertAsset(ctx context.Context, in *UpsertAssetRequest, opts ...grpc.CallOption) (*UpsertAssetResponse, error) UpsertPatchAsset(ctx context.Context, in *UpsertPatchAssetRequest, opts ...grpc.CallOption) (*UpsertPatchAssetResponse, error) DeleteAsset(ctx context.Context, in *DeleteAssetRequest, opts ...grpc.CallOption) (*DeleteAssetResponse, error) + DeleteAssets(ctx context.Context, in *DeleteAssetsRequest, opts ...grpc.CallOption) (*DeleteAssetsResponse, error) GetAssetStargazers(ctx context.Context, in *GetAssetStargazersRequest, opts ...grpc.CallOption) (*GetAssetStargazersResponse, error) GetAssetVersionHistory(ctx context.Context, in *GetAssetVersionHistoryRequest, opts ...grpc.CallOption) (*GetAssetVersionHistoryResponse, error) GetAssetByVersion(ctx context.Context, in *GetAssetByVersionRequest, opts ...grpc.CallOption) (*GetAssetByVersionResponse, error) @@ -291,6 +293,15 @@ func (c *compassServiceClient) DeleteAsset(ctx context.Context, in *DeleteAssetR return out, nil } +func (c *compassServiceClient) DeleteAssets(ctx context.Context, in *DeleteAssetsRequest, opts ...grpc.CallOption) (*DeleteAssetsResponse, error) { + out := new(DeleteAssetsResponse) + err := c.cc.Invoke(ctx, CompassService_DeleteAssets_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *compassServiceClient) GetAssetStargazers(ctx context.Context, in *GetAssetStargazersRequest, opts ...grpc.CallOption) (*GetAssetStargazersResponse, error) { out := new(GetAssetStargazersResponse) err := c.cc.Invoke(ctx, CompassService_GetAssetStargazers_FullMethodName, in, out, opts...) @@ -506,6 +517,7 @@ type CompassServiceServer interface { UpsertAsset(context.Context, *UpsertAssetRequest) (*UpsertAssetResponse, error) UpsertPatchAsset(context.Context, *UpsertPatchAssetRequest) (*UpsertPatchAssetResponse, error) DeleteAsset(context.Context, *DeleteAssetRequest) (*DeleteAssetResponse, error) + DeleteAssets(context.Context, *DeleteAssetsRequest) (*DeleteAssetsResponse, error) GetAssetStargazers(context.Context, *GetAssetStargazersRequest) (*GetAssetStargazersResponse, error) GetAssetVersionHistory(context.Context, *GetAssetVersionHistoryRequest) (*GetAssetVersionHistoryResponse, error) GetAssetByVersion(context.Context, *GetAssetByVersionRequest) (*GetAssetByVersionResponse, error) @@ -593,6 +605,9 @@ func (UnimplementedCompassServiceServer) UpsertPatchAsset(context.Context, *Upse func (UnimplementedCompassServiceServer) DeleteAsset(context.Context, *DeleteAssetRequest) (*DeleteAssetResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteAsset not implemented") } +func (UnimplementedCompassServiceServer) DeleteAssets(context.Context, *DeleteAssetsRequest) (*DeleteAssetsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteAssets not implemented") +} func (UnimplementedCompassServiceServer) GetAssetStargazers(context.Context, *GetAssetStargazersRequest) (*GetAssetStargazersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetAssetStargazers not implemented") } @@ -1011,6 +1026,24 @@ func _CompassService_DeleteAsset_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } +func _CompassService_DeleteAssets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteAssetsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CompassServiceServer).DeleteAssets(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: CompassService_DeleteAssets_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CompassServiceServer).DeleteAssets(ctx, req.(*DeleteAssetsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _CompassService_GetAssetStargazers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetAssetStargazersRequest) if err := dec(in); err != nil { @@ -1472,6 +1505,10 @@ var CompassService_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteAsset", Handler: _CompassService_DeleteAsset_Handler, }, + { + MethodName: "DeleteAssets", + Handler: _CompassService_DeleteAssets_Handler, + }, { MethodName: "GetAssetStargazers", Handler: _CompassService_GetAssetStargazers_Handler, From 573ecace85a1f19c168a12106cdd0585a9f2fe6c Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Thu, 8 Aug 2024 14:55:57 +0700 Subject: [PATCH 02/45] feat(asset): add refreshed_at field --- .../migrations/000019_alter_asset_table_refreshed_at.down.sql | 1 + .../migrations/000019_alter_asset_table_refreshed_at.up.sql | 1 + 2 files changed, 2 insertions(+) create mode 100644 internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql create mode 100644 internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.up.sql diff --git a/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql b/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql new file mode 100644 index 00000000..261f9290 --- /dev/null +++ b/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql @@ -0,0 +1 @@ +ALTER TABLE assets ADD COLUMN IF NOT EXISTS refreshed_at TIMESTAMP DEFAULT NOW(); \ No newline at end of file diff --git a/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.up.sql b/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.up.sql new file mode 100644 index 00000000..db1fb5e9 --- /dev/null +++ b/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.up.sql @@ -0,0 +1 @@ +ALTER TABLE assets DROP COLUMN IF EXISTS refreshed_at; From 1bc3990e02345486a90ff0fc42cbb5ce667c9a95 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Thu, 8 Aug 2024 14:56:09 +0700 Subject: [PATCH 03/45] feat: create translator from query expr to SQL query and ES query --- pkg/translator/query_expr_translator.go | 364 ++++++++++++++++++ pkg/translator/query_expr_translator_test.go | 125 ++++++ .../test-json/equals-or-not-in-condition.json | 28 ++ pkg/translator/test-json/in-condition.json | 11 + pkg/translator/test-json/lt-condition.json | 9 + 5 files changed, 537 insertions(+) create mode 100644 pkg/translator/query_expr_translator.go create mode 100644 pkg/translator/query_expr_translator_test.go create mode 100644 pkg/translator/test-json/equals-or-not-in-condition.json create mode 100644 pkg/translator/test-json/in-condition.json create mode 100644 pkg/translator/test-json/lt-condition.json diff --git a/pkg/translator/query_expr_translator.go b/pkg/translator/query_expr_translator.go new file mode 100644 index 00000000..cda07941 --- /dev/null +++ b/pkg/translator/query_expr_translator.go @@ -0,0 +1,364 @@ +package translator + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/ast" + "github.com/expr-lang/expr/parser" + "log" + "strconv" + "strings" +) + +type QueryExprTranslator struct { + QueryExpr string + SqlQuery strings.Builder + EsQuery map[string]interface{} +} + +type ExprParam map[string]interface{} + +func (q *QueryExprTranslator) getTreeNodeFromQueryExpr() *ast.Node { + parsed, err := parser.Parse(q.QueryExpr) + if err != nil { + log.Fatalf("Error parsing expression: %v", err) + } + + return &parsed.Node +} + +func (q *QueryExprTranslator) ConvertToSQL() (string, error) { + q.SqlQuery = strings.Builder{} + q.TranslateToSQL(q.getTreeNodeFromQueryExpr(), q) + return q.SqlQuery.String(), nil +} + +func (q *QueryExprTranslator) TranslateToSQL(node *ast.Node, translator *QueryExprTranslator) { + if *node == nil { + return + } + switch n := (*node).(type) { + case *ast.BinaryNode: + translator.SqlQuery.WriteString("(") + q.TranslateToSQL(&n.Left, translator) + + // write operator + operator := q.operatorToSQL(n) + translator.SqlQuery.WriteString(fmt.Sprintf(" %s ", strings.ToUpper(operator))) + + q.TranslateToSQL(&n.Right, translator) + translator.SqlQuery.WriteString(")") + case *ast.NilNode: + translator.SqlQuery.WriteString(fmt.Sprintf("%s", "NULL")) + case *ast.IdentifierNode: + translator.SqlQuery.WriteString(n.Value) + case *ast.IntegerNode: + translator.SqlQuery.WriteString(strconv.FormatInt(int64(n.Value), 10)) + case *ast.FloatNode: + translator.SqlQuery.WriteString(strconv.FormatFloat(n.Value, 'f', -1, 64)) + case *ast.BoolNode: + translator.SqlQuery.WriteString(strconv.FormatBool(n.Value)) + case *ast.StringNode: + translator.SqlQuery.WriteString(fmt.Sprintf("'%s'", n.Value)) + case *ast.ConstantNode: + translator.SqlQuery.WriteString(fmt.Sprintf("%s", n.Value)) + case *ast.UnaryNode: + switch n.Operator { + case "not": + binaryNode, ok := (n.Node).(*ast.BinaryNode) + if ok && binaryNode.Operator == "in" { + ast.Patch(&n.Node, &ast.BinaryNode{ + Operator: "not in", + Left: binaryNode.Left, + Right: binaryNode.Right, + }) + } + case "!": + switch nodeV := n.Node.(type) { + case *ast.BoolNode: + ast.Patch(&n.Node, &ast.BoolNode{ + Value: !nodeV.Value, + }) + // adjust other type if needed + } + } + q.TranslateToSQL(&n.Node, translator) + case *ast.BuiltinNode: + result, err := q.getQueryExprResult(n.String()) + if err != nil { + return + } + translator.SqlQuery.WriteString(fmt.Sprintf("%s", result)) + case *ast.ArrayNode: + translator.SqlQuery.WriteString("(") + for i := range n.Nodes { + q.TranslateToSQL(&n.Nodes[i], translator) + if i != len(n.Nodes)-1 { + translator.SqlQuery.WriteString(", ") + } + } + translator.SqlQuery.WriteString(")") + case *ast.ConditionalNode: + result, err := q.getQueryExprResult(n.String()) + if err != nil { + return + } + if nodeV, ok := result.(ast.Node); ok { + q.TranslateToSQL(&nodeV, translator) + } + case *ast.ChainNode: + case *ast.MemberNode: + case *ast.SliceNode: + case *ast.CallNode: + case *ast.ClosureNode: + case *ast.PointerNode: + case *ast.VariableDeclaratorNode: + case *ast.MapNode: + case *ast.PairNode: + default: + panic(fmt.Sprintf("undefined node type (%T)", node)) + } +} + +func (q *QueryExprTranslator) getQueryExprResult(fn string) (any, error) { + env := make(ExprParam) + compile, err := expr.Compile(fn) + if err != nil { + return nil, fmt.Errorf("failed to compile function '%s': %w", fn, err) + } + + result, err := expr.Run(compile, env) + if err != nil { + return nil, fmt.Errorf("failed to evaluate function '%s': %w", fn, err) + } + + return result, nil +} + +func (q *QueryExprTranslator) operatorToSQL(bn *ast.BinaryNode) string { + switch { + case bn.Operator == "&&": + return "AND" + case bn.Operator == "||": + return "OR" + case bn.Operator == "!=": + if _, ok := bn.Right.(*ast.NilNode); ok { + return "IS NOT" + } + case bn.Operator == "==": + if _, ok := bn.Right.(*ast.NilNode); ok { + return "IS" + } else { + return "=" + } + } + + return bn.Operator +} + +func (q *QueryExprTranslator) ConvertToEsQuery() (string, error) { + esQueryInterface := q.TranslateToEsQuery(q.getTreeNodeFromQueryExpr()) + esQuery, ok := esQueryInterface.(map[string]interface{}) + if !ok { + return "", errors.New("failed to generate Elasticsearch query") + } + q.EsQuery = map[string]interface{}{"query": esQuery} + + // Convert to JSON + queryJSON, err := json.Marshal(q.EsQuery) + if err != nil { + return "", err + } + + return string(queryJSON), nil +} + +func (q *QueryExprTranslator) TranslateToEsQuery(node *ast.Node) interface{} { + if *node == nil { + return nil + } + switch n := (*node).(type) { + case *ast.BinaryNode: + return q.translateBinaryNodeToEsQuery(n) + case *ast.NilNode: + return nil + case *ast.IdentifierNode: + return n.Value + case *ast.IntegerNode: + return n.Value + case *ast.FloatNode: + return n.Value + case *ast.BoolNode: + return n.Value + case *ast.StringNode: + return n.Value + case *ast.UnaryNode: + return q.translateUnaryNodeToEsQuery(n) + case *ast.ArrayNode: + return q.translateArrayNodeToEsQuery(n) + case *ast.ConstantNode: + return n.Value + case *ast.BuiltinNode: + result, err := q.getQueryExprResult(n.String()) + if err != nil { + return nil + } + return result + case *ast.ConditionalNode: + result, err := q.getQueryExprResult(n.String()) + if err != nil { + return nil + } + if nodeV, ok := result.(ast.Node); ok { + return q.TranslateToEsQuery(&nodeV) + } + case *ast.ChainNode: + case *ast.MemberNode: + case *ast.SliceNode: + case *ast.CallNode: + case *ast.ClosureNode: + case *ast.PointerNode: + case *ast.VariableDeclaratorNode: + case *ast.MapNode: + case *ast.PairNode: + default: + panic(fmt.Sprintf("undefined node type (%T)", node)) + } + + return nil +} + +func (q *QueryExprTranslator) translateBinaryNodeToEsQuery(n *ast.BinaryNode) map[string]interface{} { + left := q.TranslateToEsQuery(&n.Left) + right := q.TranslateToEsQuery(&n.Right) + + switch n.Operator { + case "&&": + return q.boolQuery("must", left, right) + case "||": + return q.boolQuery("should", left, right) + case "==": + return q.termQuery(left.(string), right) + case "!=": + return q.mustNotQuery(left.(string), right) + case "<", "<=", ">", ">=": + return q.rangeQuery(left.(string), q.operatorToEsQuery(n.Operator), right) + case "in": + return q.termsQuery(left.(string), right) + default: + return nil + } +} + +func (q *QueryExprTranslator) translateUnaryNodeToEsQuery(n *ast.UnaryNode) interface{} { + switch n.Operator { + case "not": + if binaryNode, ok := n.Node.(*ast.BinaryNode); ok && binaryNode.Operator == "in" { + left := q.TranslateToEsQuery(&binaryNode.Left) + right := q.TranslateToEsQuery(&binaryNode.Right) + return q.mustNotTermsQuery(left.(string), right) + } + return nil + case "!": + nodeValue := q.TranslateToEsQuery(&n.Node) + switch value := nodeValue.(type) { + case bool: + return !value + default: + return map[string]interface{}{ + "bool": map[string]interface{}{ + "must_not": []interface{}{nodeValue}, + }, + } + } + default: + return nil + } +} + +func (q *QueryExprTranslator) translateArrayNodeToEsQuery(n *ast.ArrayNode) []interface{} { + values := make([]interface{}, len(n.Nodes)) + for i, node := range n.Nodes { + values[i] = q.TranslateToEsQuery(&node) + } + return values +} + +func (q *QueryExprTranslator) operatorToEsQuery(operator string) string { + switch operator { + case ">": + return "gt" + case ">=": + return "gte" + case "<": + return "lt" + case "<=": + return "lte" + } + + return operator +} + +func (q *QueryExprTranslator) boolQuery(condition string, left, right interface{}) map[string]interface{} { + return map[string]interface{}{ + "bool": map[string]interface{}{ + condition: []interface{}{left, right}, + }, + } +} + +func (q *QueryExprTranslator) termQuery(field string, value interface{}) map[string]interface{} { + return map[string]interface{}{ + "term": map[string]interface{}{ + field: value, + }, + } +} + +func (q *QueryExprTranslator) mustNotQuery(field string, value interface{}) map[string]interface{} { + return map[string]interface{}{ + "bool": map[string]interface{}{ + "must_not": []interface{}{ + map[string]interface{}{ + "term": map[string]interface{}{ + field: value, + }, + }, + }, + }, + } +} + +func (q *QueryExprTranslator) rangeQuery(field, operator string, value interface{}) map[string]interface{} { + return map[string]interface{}{ + "range": map[string]interface{}{ + field: map[string]interface{}{ + operator: value, + }, + }, + } +} + +func (q *QueryExprTranslator) termsQuery(field string, values interface{}) map[string]interface{} { + return map[string]interface{}{ + "terms": map[string]interface{}{ + field: values, + }, + } +} + +func (q *QueryExprTranslator) mustNotTermsQuery(field string, values interface{}) map[string]interface{} { + return map[string]interface{}{ + "bool": map[string]interface{}{ + "must_not": []interface{}{ + map[string]interface{}{ + "terms": map[string]interface{}{ + field: values, + }, + }, + }, + }, + } +} diff --git a/pkg/translator/query_expr_translator_test.go b/pkg/translator/query_expr_translator_test.go new file mode 100644 index 00000000..23d329ed --- /dev/null +++ b/pkg/translator/query_expr_translator_test.go @@ -0,0 +1,125 @@ +package translator + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "reflect" + "testing" +) + +func TestQueryExprTranslator_ConvertToEsQuery(t *testing.T) { + tests := []struct { + name string + queryExprTranslator *QueryExprTranslator + want string + wantErr bool + }{ + { + name: "less than condition", + queryExprTranslator: &QueryExprTranslator{ + QueryExpr: `updated_at < "2024-04-05 23:59:59"`, + }, + want: "test-json/lt-condition.json", + wantErr: false, + }, + { + name: "in condition", + queryExprTranslator: &QueryExprTranslator{ + QueryExpr: `service in ["test1","test2","test3"]`, + }, + want: "test-json/in-condition.json", + wantErr: false, + }, + { + name: "equals or not in condition", + queryExprTranslator: &QueryExprTranslator{ + QueryExpr: `name == "John" || service not in ["test1","test2","test3"]`, + }, + want: "test-json/equals-or-not-in-condition.json", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.queryExprTranslator.ConvertToEsQuery() + if (err != nil) != tt.wantErr { + t.Errorf("ConvertToEsQuery() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !deepEqual(tt.want, tt.queryExprTranslator.EsQuery) { + t.Errorf("ConvertToEsQuery() got = %v, want equal to json in file: %v", got, tt.want) + } + }) + } +} + +func TestQueryExprTranslator_ConvertToSQL(t *testing.T) { + tests := []struct { + name string + queryExprTranslator *QueryExprTranslator + want string + wantErr bool + }{ + { + name: "less than condition", + queryExprTranslator: &QueryExprTranslator{ + QueryExpr: `updated_at < "2024-04-05 23:59:59"`, + }, + want: `(updated_at < '2024-04-05 23:59:59')`, + wantErr: false, + }, + { + name: "in condition", + queryExprTranslator: &QueryExprTranslator{ + QueryExpr: `service in ["test1","test2","test3"]`, + }, + want: `(service IN ('test1', 'test2', 'test3'))`, + wantErr: false, + }, + { + name: "equals or not in condition", + queryExprTranslator: &QueryExprTranslator{ + QueryExpr: `name == "John" || service not in ["test1","test2","test3"]`, + }, + want: `((name = 'John') OR (service NOT IN ('test1', 'test2', 'test3')))`, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.queryExprTranslator.ConvertToSQL() + if (err != nil) != tt.wantErr { + t.Errorf("ConvertToSQL() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ConvertToSQL() got = %v, want %v", got, tt.want) + } + }) + } +} + +func deepEqual(jsonFileName string, result map[string]interface{}) bool { + // Step 1: Read the JSON file + fileContent, err := ioutil.ReadFile(jsonFileName) + if err != nil { + fmt.Println("Error reading the file:", err) + return false + } + + // Step 2: Unmarshal the file content into a Go data structure + var fileData map[string]interface{} + err = json.Unmarshal(fileContent, &fileData) + if err != nil { + fmt.Println("Error unmarshalling the file content:", err) + return false + } + + // Step 4: Compare the two Go data structures + if reflect.DeepEqual(fileData, result) { + return true + } else { + return false + } +} diff --git a/pkg/translator/test-json/equals-or-not-in-condition.json b/pkg/translator/test-json/equals-or-not-in-condition.json new file mode 100644 index 00000000..6942cafb --- /dev/null +++ b/pkg/translator/test-json/equals-or-not-in-condition.json @@ -0,0 +1,28 @@ +{ + "query": { + "bool": { + "should": [ + { + "term": { + "name": "John" + } + }, + { + "bool": { + "must_not": [ + { + "terms": { + "service": [ + "test1", + "test2", + "test3" + ] + } + } + ] + } + } + ] + } + } +} \ No newline at end of file diff --git a/pkg/translator/test-json/in-condition.json b/pkg/translator/test-json/in-condition.json new file mode 100644 index 00000000..33fd2db7 --- /dev/null +++ b/pkg/translator/test-json/in-condition.json @@ -0,0 +1,11 @@ +{ + "query": { + "terms": { + "service": [ + "test1", + "test2", + "test3" + ] + } + } +} \ No newline at end of file diff --git a/pkg/translator/test-json/lt-condition.json b/pkg/translator/test-json/lt-condition.json new file mode 100644 index 00000000..d29da73a --- /dev/null +++ b/pkg/translator/test-json/lt-condition.json @@ -0,0 +1,9 @@ +{ + "query": { + "range": { + "updated_at": { + "lt": "2024-04-05 23:59:59" + } + } + } +} \ No newline at end of file From 17918ce38110db1dea50895da8f439782210630e Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Thu, 8 Aug 2024 15:08:05 +0700 Subject: [PATCH 04/45] feat(module): add expr-lang/expr module --- go.mod | 1 + go.sum | 2 ++ 2 files changed, 3 insertions(+) diff --git a/go.mod b/go.mod index e4fdcff0..8deaea14 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/Masterminds/squirrel v1.5.2 github.com/elastic/go-elasticsearch/v7 v7.16.0 github.com/envoyproxy/protoc-gen-validate v0.10.1 + github.com/expr-lang/expr v1.16.9 github.com/go-playground/locales v0.14.0 github.com/go-playground/universal-translator v0.18.0 github.com/go-playground/validator/v10 v10.10.0 diff --git a/go.sum b/go.sum index 390f1446..9290d1be 100644 --- a/go.sum +++ b/go.sum @@ -471,6 +471,8 @@ github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byA github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= +github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= From cd81a695b5fe6e8e445100b72e0bfd6be2a1e5c6 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Thu, 8 Aug 2024 15:28:56 +0700 Subject: [PATCH 05/45] feat(docs): add comments in QueryExprTranslator --- pkg/translator/query_expr_translator.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/translator/query_expr_translator.go b/pkg/translator/query_expr_translator.go index cda07941..78d508b3 100644 --- a/pkg/translator/query_expr_translator.go +++ b/pkg/translator/query_expr_translator.go @@ -35,6 +35,8 @@ func (q *QueryExprTranslator) ConvertToSQL() (string, error) { return q.SqlQuery.String(), nil } +// TranslateToSQL The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. +// TODO: implement translator for node type that still not covered right now. func (q *QueryExprTranslator) TranslateToSQL(node *ast.Node, translator *QueryExprTranslator) { if *node == nil { return @@ -175,6 +177,8 @@ func (q *QueryExprTranslator) ConvertToEsQuery() (string, error) { return string(queryJSON), nil } +// TranslateToEsQuery The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. +// TODO: implement translator for node type that still not covered right now. func (q *QueryExprTranslator) TranslateToEsQuery(node *ast.Node) interface{} { if *node == nil { return nil From ebb14c7a32469f8495431080e2868c27fcd0497f Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Thu, 8 Aug 2024 15:54:36 +0700 Subject: [PATCH 06/45] feat(asset): change migration update --- .../migrations/000019_alter_asset_table_refreshed_at.down.sql | 1 - .../migrations/000019_alter_asset_table_refreshed_at.up.sql | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql diff --git a/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql b/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql deleted file mode 100644 index 261f9290..00000000 --- a/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE assets ADD COLUMN IF NOT EXISTS refreshed_at TIMESTAMP DEFAULT NOW(); \ No newline at end of file diff --git a/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.up.sql b/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.up.sql index db1fb5e9..0a2cc856 100644 --- a/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.up.sql +++ b/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.up.sql @@ -1 +1 @@ -ALTER TABLE assets DROP COLUMN IF EXISTS refreshed_at; +ALTER TABLE assets ADD COLUMN IF NOT EXISTS refreshed_at TIMESTAMP; \ No newline at end of file From 94e9e7b57d17a21d3d1c07f08e549a7ba814a22e Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Thu, 8 Aug 2024 15:56:58 +0700 Subject: [PATCH 07/45] feat(asset): add down migration --- .../migrations/000019_alter_asset_table_refreshed_at.down.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql diff --git a/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql b/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql new file mode 100644 index 00000000..db1fb5e9 --- /dev/null +++ b/internal/store/postgres/migrations/000019_alter_asset_table_refreshed_at.down.sql @@ -0,0 +1 @@ +ALTER TABLE assets DROP COLUMN IF EXISTS refreshed_at; From b2094e744fbdfa4ef6c93e22b02de2c7aac4aa9a Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Thu, 8 Aug 2024 16:01:58 +0700 Subject: [PATCH 08/45] feat(translator): mark as private func for func that only used in translator --- pkg/translator/query_expr_translator.go | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pkg/translator/query_expr_translator.go b/pkg/translator/query_expr_translator.go index 78d508b3..16c823bd 100644 --- a/pkg/translator/query_expr_translator.go +++ b/pkg/translator/query_expr_translator.go @@ -31,26 +31,26 @@ func (q *QueryExprTranslator) getTreeNodeFromQueryExpr() *ast.Node { func (q *QueryExprTranslator) ConvertToSQL() (string, error) { q.SqlQuery = strings.Builder{} - q.TranslateToSQL(q.getTreeNodeFromQueryExpr(), q) + q.translateToSQL(q.getTreeNodeFromQueryExpr(), q) return q.SqlQuery.String(), nil } -// TranslateToSQL The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. +// translateToSQL The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. // TODO: implement translator for node type that still not covered right now. -func (q *QueryExprTranslator) TranslateToSQL(node *ast.Node, translator *QueryExprTranslator) { +func (q *QueryExprTranslator) translateToSQL(node *ast.Node, translator *QueryExprTranslator) { if *node == nil { return } switch n := (*node).(type) { case *ast.BinaryNode: translator.SqlQuery.WriteString("(") - q.TranslateToSQL(&n.Left, translator) + q.translateToSQL(&n.Left, translator) // write operator operator := q.operatorToSQL(n) translator.SqlQuery.WriteString(fmt.Sprintf(" %s ", strings.ToUpper(operator))) - q.TranslateToSQL(&n.Right, translator) + q.translateToSQL(&n.Right, translator) translator.SqlQuery.WriteString(")") case *ast.NilNode: translator.SqlQuery.WriteString(fmt.Sprintf("%s", "NULL")) @@ -86,7 +86,7 @@ func (q *QueryExprTranslator) TranslateToSQL(node *ast.Node, translator *QueryEx // adjust other type if needed } } - q.TranslateToSQL(&n.Node, translator) + q.translateToSQL(&n.Node, translator) case *ast.BuiltinNode: result, err := q.getQueryExprResult(n.String()) if err != nil { @@ -96,7 +96,7 @@ func (q *QueryExprTranslator) TranslateToSQL(node *ast.Node, translator *QueryEx case *ast.ArrayNode: translator.SqlQuery.WriteString("(") for i := range n.Nodes { - q.TranslateToSQL(&n.Nodes[i], translator) + q.translateToSQL(&n.Nodes[i], translator) if i != len(n.Nodes)-1 { translator.SqlQuery.WriteString(", ") } @@ -108,7 +108,7 @@ func (q *QueryExprTranslator) TranslateToSQL(node *ast.Node, translator *QueryEx return } if nodeV, ok := result.(ast.Node); ok { - q.TranslateToSQL(&nodeV, translator) + q.translateToSQL(&nodeV, translator) } case *ast.ChainNode: case *ast.MemberNode: @@ -161,7 +161,7 @@ func (q *QueryExprTranslator) operatorToSQL(bn *ast.BinaryNode) string { } func (q *QueryExprTranslator) ConvertToEsQuery() (string, error) { - esQueryInterface := q.TranslateToEsQuery(q.getTreeNodeFromQueryExpr()) + esQueryInterface := q.translateToEsQuery(q.getTreeNodeFromQueryExpr()) esQuery, ok := esQueryInterface.(map[string]interface{}) if !ok { return "", errors.New("failed to generate Elasticsearch query") @@ -177,9 +177,9 @@ func (q *QueryExprTranslator) ConvertToEsQuery() (string, error) { return string(queryJSON), nil } -// TranslateToEsQuery The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. +// translateToEsQuery The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. // TODO: implement translator for node type that still not covered right now. -func (q *QueryExprTranslator) TranslateToEsQuery(node *ast.Node) interface{} { +func (q *QueryExprTranslator) translateToEsQuery(node *ast.Node) interface{} { if *node == nil { return nil } @@ -216,7 +216,7 @@ func (q *QueryExprTranslator) TranslateToEsQuery(node *ast.Node) interface{} { return nil } if nodeV, ok := result.(ast.Node); ok { - return q.TranslateToEsQuery(&nodeV) + return q.translateToEsQuery(&nodeV) } case *ast.ChainNode: case *ast.MemberNode: @@ -235,8 +235,8 @@ func (q *QueryExprTranslator) TranslateToEsQuery(node *ast.Node) interface{} { } func (q *QueryExprTranslator) translateBinaryNodeToEsQuery(n *ast.BinaryNode) map[string]interface{} { - left := q.TranslateToEsQuery(&n.Left) - right := q.TranslateToEsQuery(&n.Right) + left := q.translateToEsQuery(&n.Left) + right := q.translateToEsQuery(&n.Right) switch n.Operator { case "&&": @@ -260,13 +260,13 @@ func (q *QueryExprTranslator) translateUnaryNodeToEsQuery(n *ast.UnaryNode) inte switch n.Operator { case "not": if binaryNode, ok := n.Node.(*ast.BinaryNode); ok && binaryNode.Operator == "in" { - left := q.TranslateToEsQuery(&binaryNode.Left) - right := q.TranslateToEsQuery(&binaryNode.Right) + left := q.translateToEsQuery(&binaryNode.Left) + right := q.translateToEsQuery(&binaryNode.Right) return q.mustNotTermsQuery(left.(string), right) } return nil case "!": - nodeValue := q.TranslateToEsQuery(&n.Node) + nodeValue := q.translateToEsQuery(&n.Node) switch value := nodeValue.(type) { case bool: return !value @@ -285,7 +285,7 @@ func (q *QueryExprTranslator) translateUnaryNodeToEsQuery(n *ast.UnaryNode) inte func (q *QueryExprTranslator) translateArrayNodeToEsQuery(n *ast.ArrayNode) []interface{} { values := make([]interface{}, len(n.Nodes)) for i, node := range n.Nodes { - values[i] = q.TranslateToEsQuery(&node) + values[i] = q.translateToEsQuery(&node) } return values } From 8e4782f166f04477088e9224d7edc822f67f8f57 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Thu, 8 Aug 2024 18:40:21 +0700 Subject: [PATCH 09/45] feat: create delete assets API by query expr --- core/asset/asset.go | 3 + core/asset/discovery.go | 1 + core/asset/errors.go | 1 + core/asset/service.go | 62 +++++- internal/server/v1beta1/asset.go | 23 +++ .../elasticsearch/discovery_repository.go | 8 + internal/store/postgres/asset_model.go | 22 ++- internal/store/postgres/asset_repository.go | 176 +++++++++++++++--- internal/workermanager/discovery_worker.go | 34 ++++ internal/workermanager/job_types.go | 7 +- internal/workermanager/worker_manager.go | 7 +- pkg/generic_helper/generic_helper.go | 11 ++ pkg/translator/query_expr_translator.go | 20 +- 13 files changed, 333 insertions(+), 42 deletions(-) create mode 100644 pkg/generic_helper/generic_helper.go diff --git a/core/asset/asset.go b/core/asset/asset.go index 2bd17c30..6fddaf23 100644 --- a/core/asset/asset.go +++ b/core/asset/asset.go @@ -12,6 +12,7 @@ import ( type Repository interface { GetAll(context.Context, Filter) ([]Asset, error) GetCount(context.Context, Filter) (int, error) + GetCountByQuery(context.Context, string) (int, error) GetByID(ctx context.Context, id string) (Asset, error) GetByURN(ctx context.Context, urn string) (Asset, error) GetVersionHistory(ctx context.Context, flt Filter, id string) ([]Asset, error) @@ -21,6 +22,7 @@ type Repository interface { Upsert(ctx context.Context, ast *Asset) (string, error) DeleteByID(ctx context.Context, id string) error DeleteByURN(ctx context.Context, urn string) error + DeleteByQuery(ctx context.Context, whereCondition string) ([]string, error) AddProbe(ctx context.Context, assetURN string, probe *Probe) error GetProbes(ctx context.Context, assetURN string) ([]Probe, error) GetProbesWithFilter(ctx context.Context, flt ProbesFilter) (map[string][]Probe, error) @@ -40,6 +42,7 @@ type Asset struct { Owners []user.User `json:"owners,omitempty" diff:"owners"` CreatedAt time.Time `json:"created_at" diff:"-"` UpdatedAt time.Time `json:"updated_at" diff:"-"` + RefreshedAt time.Time `json:"refreshed_at" diff:"-"` Version string `json:"version" diff:"-"` UpdatedBy user.User `json:"updated_by" diff:"-"` Changelog diff.Changelog `json:"changelog,omitempty" diff:"-"` diff --git a/core/asset/discovery.go b/core/asset/discovery.go index a1c49848..4825a241 100644 --- a/core/asset/discovery.go +++ b/core/asset/discovery.go @@ -9,6 +9,7 @@ type DiscoveryRepository interface { Upsert(context.Context, Asset) error DeleteByID(ctx context.Context, assetID string) error DeleteByURN(ctx context.Context, assetURN string) error + DeleteByQuery(ctx context.Context, filterQuery string) error Search(ctx context.Context, cfg SearchConfig) (results []SearchResult, err error) Suggest(ctx context.Context, cfg SearchConfig) (suggestions []string, err error) GroupAssets(ctx context.Context, cfg GroupConfig) (results []GroupResult, err error) diff --git a/core/asset/errors.go b/core/asset/errors.go index db5f8827..26d03cd7 100644 --- a/core/asset/errors.go +++ b/core/asset/errors.go @@ -10,6 +10,7 @@ var ( ErrEmptyID = errors.New("asset does not have ID") ErrProbeExists = errors.New("asset probe already exists") ErrEmptyURN = errors.New("asset does not have URN") + ErrEmptyQuery = errors.New("query must exist to filtering assets") ErrUnknownType = errors.New("unknown type") ErrNilAsset = errors.New("nil asset") ) diff --git a/core/asset/service.go b/core/asset/service.go index a12ac61c..2d22b92c 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -3,11 +3,13 @@ package asset import ( "context" "fmt" - "github.com/google/uuid" + "github.com/goto/compass/pkg/generic_helper" + "github.com/goto/compass/pkg/translator" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "time" ) type Service struct { @@ -24,6 +26,7 @@ type Service struct { type Worker interface { EnqueueIndexAssetJob(ctx context.Context, ast Asset) error EnqueueDeleteAssetJob(ctx context.Context, urn string) error + EnqueueDeleteAssetsByQueryJob(ctx context.Context, filterQuery string) error EnqueueSyncAssetJob(ctx context.Context, service string) error Close() error } @@ -83,6 +86,9 @@ func (s *Service) UpsertAsset(ctx context.Context, ast *Asset, upstreams, downst } func (s *Service) UpsertAssetWithoutLineage(ctx context.Context, ast *Asset) (string, error) { + // Update the asset in both postgresql and elasticsearch for each upsert + ast.RefreshedAt = time.Now() + assetID, err := s.assetRepository.Upsert(ctx, ast) if err != nil { return "", err @@ -122,6 +128,60 @@ func (s *Service) DeleteAsset(ctx context.Context, id string) (err error) { return s.lineageRepository.DeleteByURN(ctx, urn) } +func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun bool) (affectedRows uint32, err error) { + queryExprTranslator := &translator.QueryExprTranslator{ + QueryExpr: queryExpr, + } + + // Check existence of required identifiers + identifiers := queryExprTranslator.GetIdentifiers() + mustExist := generic_helper.Contains(identifiers, "refreshed_at") && + generic_helper.Contains(identifiers, "type") && + generic_helper.Contains(identifiers, "service") + if !mustExist { + return 0, fmt.Errorf( + "must exists these identifiers: refreshed_at, type. Current identifiers: %v", identifiers) + } + + sqlWhereCondition, err := queryExprTranslator.ConvertToSQL() + if err != nil { + return 0, err + } + + total, err := s.assetRepository.GetCountByQuery(ctx, sqlWhereCondition) + if err != nil { + return 0, err + } + + if dryRun { + return uint32(total), nil + } + + esFilterQuery, err := queryExprTranslator.ConvertToEsQuery() + if err != nil { + return 0, err + } + + go func() { + urns, err := s.assetRepository.DeleteByQuery(ctx, sqlWhereCondition) + if err != nil { + return + } + + if err := s.worker.EnqueueDeleteAssetsByQueryJob(ctx, esFilterQuery); err != nil { + return + } + + for _, urn := range urns { + if err := s.lineageRepository.DeleteByURN(ctx, urn); err != nil { + return + } + } + }() + + return uint32(total), nil +} + func (s *Service) GetAssetByID(ctx context.Context, id string) (Asset, error) { ast, err := s.assetByIDWithoutProbes(ctx, "GetAssetByID", id) if err != nil { diff --git a/internal/server/v1beta1/asset.go b/internal/server/v1beta1/asset.go index d9e31718..0d5a7b0c 100644 --- a/internal/server/v1beta1/asset.go +++ b/internal/server/v1beta1/asset.go @@ -30,6 +30,7 @@ type AssetService interface { UpsertAsset(ctx context.Context, ast *asset.Asset, upstreams, downstreams []string) (string, error) UpsertAssetWithoutLineage(ctx context.Context, ast *asset.Asset) (string, error) DeleteAsset(ctx context.Context, id string) error + DeleteAssets(ctx context.Context, queryExpr string, dryRun bool) (uint32, error) GetLineage(ctx context.Context, urn string, query asset.LineageQuery) (asset.Lineage, error) GetTypes(ctx context.Context, flt asset.Filter) (map[asset.Type]int, error) @@ -306,6 +307,28 @@ func (server *APIServer) DeleteAsset(ctx context.Context, req *compassv1beta1.De return &compassv1beta1.DeleteAssetResponse{}, nil } +func (server *APIServer) DeleteAssets(ctx context.Context, req *compassv1beta1.DeleteAssetsRequest) (*compassv1beta1.DeleteAssetsResponse, error) { + _, err := server.ValidateUserInCtx(ctx) + if err != nil { + return nil, err + } + + affectedRows, err := server.assetService.DeleteAssets(ctx, req.QueryExpr, req.DryRun) + if err != nil { + if errors.As(err, new(asset.InvalidError)) { + return nil, status.Error(codes.InvalidArgument, err.Error()) + } + if errors.As(err, new(asset.NotFoundError)) { + return nil, status.Error(codes.NotFound, err.Error()) + } + return nil, internalServerError(server.logger, err.Error()) + } + + return &compassv1beta1.DeleteAssetsResponse{ + AffectedRows: affectedRows, + }, nil +} + func (server *APIServer) CreateAssetProbe(ctx context.Context, req *compassv1beta1.CreateAssetProbeRequest) (*compassv1beta1.CreateAssetProbeResponse, error) { _, err := server.ValidateUserInCtx(ctx) if err != nil { diff --git a/internal/store/elasticsearch/discovery_repository.go b/internal/store/elasticsearch/discovery_repository.go index 368b4945..68a57d85 100644 --- a/internal/store/elasticsearch/discovery_repository.go +++ b/internal/store/elasticsearch/discovery_repository.go @@ -144,6 +144,14 @@ func (repo *DiscoveryRepository) DeleteByURN(ctx context.Context, assetURN strin return repo.deleteWithQuery(ctx, "DeleteByURN", fmt.Sprintf(`{"query":{"term":{"urn.keyword": %q}}}`, assetURN)) } +func (repo *DiscoveryRepository) DeleteByQuery(ctx context.Context, filterQuery string) error { + if filterQuery == "" { + return asset.ErrEmptyQuery + } + + return repo.deleteWithQuery(ctx, "DeleteByQuery", filterQuery) +} + func (repo *DiscoveryRepository) deleteWithQuery(ctx context.Context, discoveryOp, qry string) (err error) { defer func(start time.Time) { const op = "delete_by_query" diff --git a/internal/store/postgres/asset_model.go b/internal/store/postgres/asset_model.go index b134e81a..5637f2b0 100644 --- a/internal/store/postgres/asset_model.go +++ b/internal/store/postgres/asset_model.go @@ -27,6 +27,7 @@ type AssetModel struct { UpdatedBy UserModel `db:"updated_by"` CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` + RefreshedAt time.Time `db:"refreshed_at"` // version specific information Changelog types.JSONText `db:"changelog"` Owners types.JSONText `db:"owners"` @@ -48,6 +49,7 @@ func (a *AssetModel) toAsset(owners []user.User) asset.Asset { UpdatedBy: a.UpdatedBy.toUser(), CreatedAt: a.CreatedAt, UpdatedAt: a.UpdatedAt, + RefreshedAt: a.RefreshedAt, } } @@ -59,15 +61,16 @@ func (a *AssetModel) toAssetVersion() (asset.Asset, error) { } return asset.Asset{ - ID: a.ID, - URN: a.URN, - Type: asset.Type(a.Type), - Service: a.Service, - Version: a.Version, - Changelog: clog, - UpdatedBy: a.UpdatedBy.toUser(), - CreatedAt: a.CreatedAt, - UpdatedAt: a.UpdatedAt, + ID: a.ID, + URN: a.URN, + Type: asset.Type(a.Type), + Service: a.Service, + Version: a.Version, + Changelog: clog, + UpdatedBy: a.UpdatedBy.toUser(), + CreatedAt: a.CreatedAt, + UpdatedAt: a.UpdatedAt, + RefreshedAt: a.RefreshedAt, }, nil } @@ -98,6 +101,7 @@ func (a *AssetModel) toVersionedAsset(latestAssetVersion asset.Asset) (asset.Ass UpdatedBy: a.UpdatedBy.toUser(), CreatedAt: a.CreatedAt, UpdatedAt: a.UpdatedAt, + RefreshedAt: a.RefreshedAt, Changelog: clog, }, nil } diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index 69440246..ed42261b 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "log" "strings" "time" @@ -16,6 +17,10 @@ import ( "github.com/r3labs/diff/v2" ) +const ( + batchSize = 1000 +) + // AssetRepository is a type that manages user operation to the primary database type AssetRepository struct { client *Client @@ -112,6 +117,24 @@ func (r *AssetRepository) GetCount(ctx context.Context, flt asset.Filter) (int, return total, nil } +// GetCountByQuery retrieves number of assets for every type based on query +func (r *AssetRepository) GetCountByQuery(ctx context.Context, sqlQuery string) (int, error) { + builder := sq.Select("count(1)"). + From("assets"). + Where(sqlQuery) + query, args, err := builder.PlaceholderFormat(sq.Dollar).ToSql() + if err != nil { + return 0, fmt.Errorf("build count query: %w", err) + } + + var total int + if err := r.client.db.GetContext(ctx, &total, query, args...); err != nil { + return 0, fmt.Errorf("get asset list: %w", err) + } + + return total, nil +} + // GetByID retrieves asset by its ID func (r *AssetRepository) GetByID(ctx context.Context, id string) (asset.Asset, error) { if !isValidUUID(id) { @@ -348,6 +371,94 @@ func (r *AssetRepository) DeleteByURN(ctx context.Context, urn string) error { return nil } +func (r *AssetRepository) DeleteByQuery(ctx context.Context, whereCondition string) ([]string, error) { + var allURNs []string + err := r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { + var lastID string + for { + // Fetch a batch of rows to delete using the last ID as a marker + urns, nextLastID, err := r.getAllURNsWithBatch(ctx, whereCondition, lastID) + if err != nil { + log.Printf("Failed to get batch to delete: %v", err) + return err + } + + // If no more rows to delete, exit the loop + if len(urns) == 0 { + break + } + + if err := r.deleteByURNs(ctx, urns); err != nil { + log.Printf("Failed to delete rows: %v", err) + return err + } + + allURNs = append(allURNs, urns...) + lastID = nextLastID + } + + return nil + }) + + if err != nil { + return nil, err + } + + return allURNs, nil +} + +func (r *AssetRepository) deleteByURNs(ctx context.Context, urns []string) error { + query, args, err := sq.Delete("assets"). + Where(sq.Eq{"urn": urns}). + PlaceholderFormat(sq.Dollar). + ToSql() + if err != nil { + return fmt.Errorf("error building query: %w", err) + } + + _, err = r.client.db.ExecContext(ctx, query, args...) + if err != nil { + return err + } + + return nil +} + +// getAllURNsWithBatch retrieves list of asset URNs as filters without consider user +func (r *AssetRepository) getAllURNsWithBatch(ctx context.Context, whereCondition string, lastID string) ([]string, string, error) { + var rows []struct { + ID string `db:"id"` + URN string `db:"urn"` + } + + builder := sq.Select("id", "urn"). + From("assets"). + Where(whereCondition). + OrderBy("id ASC"). + Limit(batchSize) + + if lastID != "" { + builder = builder.Where(sq.Gt{"id": lastID}) + } + query, args, err := builder.PlaceholderFormat(sq.Dollar).ToSql() + if err != nil { + return nil, "", fmt.Errorf("error building query: %w", err) + } + + if err := r.client.db.SelectContext(ctx, &rows, query, args...); err != nil { + return nil, "", err + } + + var urns []string + var nextLastID string + for _, row := range rows { + urns = append(urns, row.URN) + nextLastID = row.ID + } + + return urns, nextLastID, nil +} + func (r *AssetRepository) AddProbe(ctx context.Context, assetURN string, probe *asset.Probe) error { probe.AssetURN = assetURN probe.CreatedAt = time.Now().UTC() @@ -481,6 +592,7 @@ func (r *AssetRepository) insert(ctx context.Context, ast *asset.Asset) (string, err := r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { ast.CreatedAt = time.Now() ast.UpdatedAt = ast.CreatedAt + ast.RefreshedAt = ast.CreatedAt query, args, err := sq.Insert("assets"). Columns("urn", "type", "service", "name", "description", "data", "url", "labels", "created_at", "updated_by", "updated_at", "version"). Values(ast.URN, ast.Type, ast.Service, ast.Name, ast.Description, ast.Data, ast.URL, ast.Labels, ast.CreatedAt, ast.UpdatedBy.ID, ast.UpdatedAt, asset.BaseVersion). @@ -532,6 +644,17 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, return nil } + onlyRefreshed := len(clog) == 1 && clog[0].Path[0] == "RefreshedAt" + if onlyRefreshed { + return r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { + if err := r.updateAsset(ctx, tx, assetID, newAsset); err != nil { + return err + } + + return nil + }) + } + return r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { // update assets newVersion, err := asset.IncreaseMinorVersion(oldAsset.Version) @@ -542,31 +665,12 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, newAsset.ID = oldAsset.ID newAsset.UpdatedAt = time.Now() - query, args, err := sq.Update("assets"). - Set("urn", newAsset.URN). - Set("type", newAsset.Type). - Set("service", newAsset.Service). - Set("name", newAsset.Name). - Set("description", newAsset.Description). - Set("data", newAsset.Data). - Set("url", newAsset.URL). - Set("labels", newAsset.Labels). - Set("updated_at", newAsset.UpdatedAt). - Set("updated_by", newAsset.UpdatedBy.ID). - Set("version", newAsset.Version). - Where(sq.Eq{"id": assetID}). - PlaceholderFormat(sq.Dollar). - ToSql() - if err != nil { - return fmt.Errorf("build query: %w", err) - } - - if err := r.execContext(ctx, tx, query, args...); err != nil { - return fmt.Errorf("error running update asset query: %w", err) + if err := r.updateAsset(ctx, tx, assetID, newAsset); err != nil { + return err } // insert versions - if err = r.insertAssetVersion(ctx, tx, newAsset, clog); err != nil { + if err := r.insertAssetVersion(ctx, tx, newAsset, clog); err != nil { return err } @@ -587,6 +691,34 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, }) } +func (r *AssetRepository) updateAsset(ctx context.Context, tx *sqlx.Tx, assetID string, newAsset *asset.Asset) error { + query, args, err := sq.Update("assets"). + Set("urn", newAsset.URN). + Set("type", newAsset.Type). + Set("service", newAsset.Service). + Set("name", newAsset.Name). + Set("description", newAsset.Description). + Set("data", newAsset.Data). + Set("url", newAsset.URL). + Set("labels", newAsset.Labels). + Set("updated_at", newAsset.UpdatedAt). + Set("refreshed_at", newAsset.RefreshedAt). + Set("updated_by", newAsset.UpdatedBy.ID). + Set("version", newAsset.Version). + Where(sq.Eq{"id": assetID}). + PlaceholderFormat(sq.Dollar). + ToSql() + if err != nil { + return fmt.Errorf("build query: %w", err) + } + + if err := r.execContext(ctx, tx, query, args...); err != nil { + return fmt.Errorf("error running update asset query: %w", err) + } + + return nil +} + func (r *AssetRepository) insertAssetVersion(ctx context.Context, execer sqlx.ExecerContext, oldAsset *asset.Asset, clog diff.Changelog) error { if oldAsset == nil { return asset.ErrNilAsset diff --git a/internal/workermanager/discovery_worker.go b/internal/workermanager/discovery_worker.go index f3210893..6a2c70a9 100644 --- a/internal/workermanager/discovery_worker.go +++ b/internal/workermanager/discovery_worker.go @@ -15,6 +15,7 @@ import ( type DiscoveryRepository interface { Upsert(context.Context, asset.Asset) error DeleteByURN(ctx context.Context, assetURN string) error + DeleteByQuery(ctx context.Context, filterQuery string) error SyncAssets(ctx context.Context, indexName string) (cleanupFn func() error, err error) } @@ -158,6 +159,39 @@ func (m *Manager) DeleteAsset(ctx context.Context, job worker.JobSpec) error { return nil } +func (m *Manager) EnqueueDeleteAssetsByQueryJob(ctx context.Context, filterQuery string) error { + err := m.worker.Enqueue(ctx, worker.JobSpec{ + Type: jobDeleteAssetsByQuery, + Payload: ([]byte)(filterQuery), + }) + if err != nil { + return fmt.Errorf("enqueue delete asset job: %w: urn '%s'", err, filterQuery) + } + + return nil +} + +func (m *Manager) deleteAssetsByQueryHandler() worker.JobHandler { + return worker.JobHandler{ + Handle: m.DeleteAssetsByQuery, + JobOpts: worker.JobOptions{ + MaxAttempts: 3, + Timeout: m.indexTimeout, + BackoffStrategy: worker.DefaultExponentialBackoff, + }, + } +} + +func (m *Manager) DeleteAssetsByQuery(ctx context.Context, job worker.JobSpec) error { + filterQuery := (string)(job.Payload) + if err := m.discoveryRepo.DeleteByQuery(ctx, filterQuery); err != nil { + return &worker.RetryableError{ + Cause: fmt.Errorf("delete asset from discovery repo: %w: query '%s'", err, filterQuery), + } + } + return nil +} + func (m *Manager) EnqueueSyncAssetJob(ctx context.Context, service string) error { err := m.worker.Enqueue(ctx, worker.JobSpec{ Type: jobSyncAsset, diff --git a/internal/workermanager/job_types.go b/internal/workermanager/job_types.go index c19fbb55..7b2849b6 100644 --- a/internal/workermanager/job_types.go +++ b/internal/workermanager/job_types.go @@ -1,7 +1,8 @@ package workermanager const ( - jobIndexAsset = "index-asset" - jobDeleteAsset = "delete-asset" - jobSyncAsset = "sync-asset" + jobIndexAsset = "index-asset" + jobDeleteAsset = "delete-asset" + jobDeleteAssetsByQuery = "delete-assets-by-query" + jobSyncAsset = "sync-asset" ) diff --git a/internal/workermanager/worker_manager.go b/internal/workermanager/worker_manager.go index 0cd15679..ed795e52 100644 --- a/internal/workermanager/worker_manager.go +++ b/internal/workermanager/worker_manager.go @@ -124,9 +124,10 @@ func (m *Manager) init() error { m.initDone.Store(true) jobHandlers := map[string]worker.JobHandler{ - jobIndexAsset: m.indexAssetHandler(), - jobDeleteAsset: m.deleteAssetHandler(), - jobSyncAsset: m.syncAssetHandler(), + jobIndexAsset: m.indexAssetHandler(), + jobDeleteAsset: m.deleteAssetHandler(), + jobDeleteAssetsByQuery: m.deleteAssetsByQueryHandler(), + jobSyncAsset: m.syncAssetHandler(), } for typ, h := range jobHandlers { if err := m.worker.Register(typ, h); err != nil { diff --git a/pkg/generic_helper/generic_helper.go b/pkg/generic_helper/generic_helper.go new file mode 100644 index 00000000..559f2c45 --- /dev/null +++ b/pkg/generic_helper/generic_helper.go @@ -0,0 +1,11 @@ +package generic_helper + +// Contains checks if a target item exists in an array of any type. +func Contains[T comparable](arr []T, target T) bool { + for _, item := range arr { + if item == target { + return true + } + } + return false +} diff --git a/pkg/translator/query_expr_translator.go b/pkg/translator/query_expr_translator.go index 16c823bd..2e06392d 100644 --- a/pkg/translator/query_expr_translator.go +++ b/pkg/translator/query_expr_translator.go @@ -12,13 +12,25 @@ import ( "strings" ) +type ExprParam map[string]interface{} + type QueryExprTranslator struct { - QueryExpr string - SqlQuery strings.Builder - EsQuery map[string]interface{} + QueryExpr string + SqlQuery strings.Builder + EsQuery map[string]interface{} + Identifiers []string } -type ExprParam map[string]interface{} +func (q *QueryExprTranslator) Visit(node *ast.Node) { + if n, ok := (*node).(*ast.IdentifierNode); ok { + q.Identifiers = append(q.Identifiers, n.Value) + } +} + +func (q *QueryExprTranslator) GetIdentifiers() []string { + ast.Walk(q.getTreeNodeFromQueryExpr(), q) + return q.Identifiers +} func (q *QueryExprTranslator) getTreeNodeFromQueryExpr() *ast.Node { parsed, err := parser.Parse(q.QueryExpr) From f068a053e1cfffc85816c163c11333bcefb9870f Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Fri, 9 Aug 2024 15:00:00 +0700 Subject: [PATCH 10/45] refactor: make interface for query expr and implement to postgresql and elasticsearch --- core/asset/asset.go | 4 +- core/asset/discovery.go | 2 +- core/asset/service.go | 34 +- .../elasticsearch/discovery_repository.go | 38 +- internal/store/postgres/asset_repository.go | 83 +++- internal/workermanager/discovery_worker.go | 18 +- internal/workermanager/in_situ_worker.go | 7 + pkg/generic_helper/generic_helper.go | 2 +- pkg/query_expr/es_expr.go | 219 ++++++++++ .../equals-or-not-in-condition.json | 0 .../es_test_data}/in-condition.json | 0 .../es_test_data}/lt-condition.json | 0 pkg/query_expr/query_expr.go | 71 ++++ pkg/query_expr/sql_expr.go | 130 ++++++ pkg/translator/query_expr_translator.go | 380 ------------------ pkg/translator/query_expr_translator_test.go | 125 ------ 16 files changed, 550 insertions(+), 563 deletions(-) create mode 100644 pkg/query_expr/es_expr.go rename pkg/{translator/test-json => query_expr/es_test_data}/equals-or-not-in-condition.json (100%) rename pkg/{translator/test-json => query_expr/es_test_data}/in-condition.json (100%) rename pkg/{translator/test-json => query_expr/es_test_data}/lt-condition.json (100%) create mode 100644 pkg/query_expr/query_expr.go create mode 100644 pkg/query_expr/sql_expr.go delete mode 100644 pkg/translator/query_expr_translator.go delete mode 100644 pkg/translator/query_expr_translator_test.go diff --git a/core/asset/asset.go b/core/asset/asset.go index 6fddaf23..794f336a 100644 --- a/core/asset/asset.go +++ b/core/asset/asset.go @@ -12,7 +12,7 @@ import ( type Repository interface { GetAll(context.Context, Filter) ([]Asset, error) GetCount(context.Context, Filter) (int, error) - GetCountByQuery(context.Context, string) (int, error) + GetCountByQueryExpr(context.Context, string, bool) (int, error) GetByID(ctx context.Context, id string) (Asset, error) GetByURN(ctx context.Context, urn string) (Asset, error) GetVersionHistory(ctx context.Context, flt Filter, id string) ([]Asset, error) @@ -22,7 +22,7 @@ type Repository interface { Upsert(ctx context.Context, ast *Asset) (string, error) DeleteByID(ctx context.Context, id string) error DeleteByURN(ctx context.Context, urn string) error - DeleteByQuery(ctx context.Context, whereCondition string) ([]string, error) + DeleteByQueryExpr(ctx context.Context, queryExpr string) ([]string, error) AddProbe(ctx context.Context, assetURN string, probe *Probe) error GetProbes(ctx context.Context, assetURN string) ([]Probe, error) GetProbesWithFilter(ctx context.Context, flt ProbesFilter) (map[string][]Probe, error) diff --git a/core/asset/discovery.go b/core/asset/discovery.go index 4825a241..c7cbeef2 100644 --- a/core/asset/discovery.go +++ b/core/asset/discovery.go @@ -9,7 +9,7 @@ type DiscoveryRepository interface { Upsert(context.Context, Asset) error DeleteByID(ctx context.Context, assetID string) error DeleteByURN(ctx context.Context, assetURN string) error - DeleteByQuery(ctx context.Context, filterQuery string) error + DeleteByQueryExpr(ctx context.Context, filterQuery string) error Search(ctx context.Context, cfg SearchConfig) (results []SearchResult, err error) Suggest(ctx context.Context, cfg SearchConfig) (suggestions []string, err error) GroupAssets(ctx context.Context, cfg GroupConfig) (results []GroupResult, err error) diff --git a/core/asset/service.go b/core/asset/service.go index 2d22b92c..aee14979 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -4,8 +4,6 @@ import ( "context" "fmt" "github.com/google/uuid" - "github.com/goto/compass/pkg/generic_helper" - "github.com/goto/compass/pkg/translator" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -26,7 +24,7 @@ type Service struct { type Worker interface { EnqueueIndexAssetJob(ctx context.Context, ast Asset) error EnqueueDeleteAssetJob(ctx context.Context, urn string) error - EnqueueDeleteAssetsByQueryJob(ctx context.Context, filterQuery string) error + EnqueueDeleteAssetsByQueryExprJob(ctx context.Context, queryExpr string) error EnqueueSyncAssetJob(ctx context.Context, service string) error Close() error } @@ -129,26 +127,7 @@ func (s *Service) DeleteAsset(ctx context.Context, id string) (err error) { } func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun bool) (affectedRows uint32, err error) { - queryExprTranslator := &translator.QueryExprTranslator{ - QueryExpr: queryExpr, - } - - // Check existence of required identifiers - identifiers := queryExprTranslator.GetIdentifiers() - mustExist := generic_helper.Contains(identifiers, "refreshed_at") && - generic_helper.Contains(identifiers, "type") && - generic_helper.Contains(identifiers, "service") - if !mustExist { - return 0, fmt.Errorf( - "must exists these identifiers: refreshed_at, type. Current identifiers: %v", identifiers) - } - - sqlWhereCondition, err := queryExprTranslator.ConvertToSQL() - if err != nil { - return 0, err - } - - total, err := s.assetRepository.GetCountByQuery(ctx, sqlWhereCondition) + total, err := s.assetRepository.GetCountByQueryExpr(ctx, queryExpr, true) if err != nil { return 0, err } @@ -157,18 +136,13 @@ func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun boo return uint32(total), nil } - esFilterQuery, err := queryExprTranslator.ConvertToEsQuery() - if err != nil { - return 0, err - } - go func() { - urns, err := s.assetRepository.DeleteByQuery(ctx, sqlWhereCondition) + urns, err := s.assetRepository.DeleteByQueryExpr(ctx, queryExpr) if err != nil { return } - if err := s.worker.EnqueueDeleteAssetsByQueryJob(ctx, esFilterQuery); err != nil { + if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(ctx, queryExpr); err != nil { return } diff --git a/internal/store/elasticsearch/discovery_repository.go b/internal/store/elasticsearch/discovery_repository.go index 68a57d85..09357c13 100644 --- a/internal/store/elasticsearch/discovery_repository.go +++ b/internal/store/elasticsearch/discovery_repository.go @@ -6,6 +6,8 @@ import ( "encoding/json" "errors" "fmt" + generichelper "github.com/goto/compass/pkg/generic_helper" + queryexpr "github.com/goto/compass/pkg/query_expr" "io" "net/url" "strings" @@ -24,6 +26,26 @@ type DiscoveryRepository struct { columnSearchExclusionList []string } +type DeleteAssetESExpr struct { + queryexpr.ESExpr +} + +func (d *DeleteAssetESExpr) Validate() error { + identifiers, err := queryexpr.GetIdentifiers(d.QueryExpr) + if err != nil { + return err + } + + mustExist := generichelper.Contains(identifiers, "refreshed_at") && + generichelper.Contains(identifiers, "type") && + generichelper.Contains(identifiers, "service") + if !mustExist { + return fmt.Errorf("must exists these identifiers: refreshed_at, type. Current identifiers: %v", identifiers) + } + + return nil +} + func NewDiscoveryRepository(cli *Client, logger log.Logger, requestTimeout time.Duration, colSearchExclusionList []string) *DiscoveryRepository { return &DiscoveryRepository{ cli: cli, @@ -144,12 +166,22 @@ func (repo *DiscoveryRepository) DeleteByURN(ctx context.Context, assetURN strin return repo.deleteWithQuery(ctx, "DeleteByURN", fmt.Sprintf(`{"query":{"term":{"urn.keyword": %q}}}`, assetURN)) } -func (repo *DiscoveryRepository) DeleteByQuery(ctx context.Context, filterQuery string) error { - if filterQuery == "" { +func (repo *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) error { + if queryExpr == "" { return asset.ErrEmptyQuery } - return repo.deleteWithQuery(ctx, "DeleteByQuery", filterQuery) + deleteAssetESExpr := &DeleteAssetESExpr{ + queryexpr.ESExpr{ + QueryExpr: queryExpr, + }, + } + esQuery, err := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) + if err != nil { + return err + } + + return repo.deleteWithQuery(ctx, "DeleteByQueryExpr", esQuery) } func (repo *DiscoveryRepository) deleteWithQuery(ctx context.Context, discoveryOp, qry string) (err error) { diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index ed42261b..4c815d85 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -6,6 +6,8 @@ import ( "encoding/json" "errors" "fmt" + generichelper "github.com/goto/compass/pkg/generic_helper" + queryexpr "github.com/goto/compass/pkg/query_expr" "log" "strings" "time" @@ -17,9 +19,7 @@ import ( "github.com/r3labs/diff/v2" ) -const ( - batchSize = 1000 -) +const batchSize = 1000 // AssetRepository is a type that manages user operation to the primary database type AssetRepository struct { @@ -29,6 +29,26 @@ type AssetRepository struct { defaultUserProvider string } +type DeleteAssetSQLExpr struct { + queryexpr.SQLExpr +} + +func (d *DeleteAssetSQLExpr) Validate() error { + identifiers, err := queryexpr.GetIdentifiers(d.QueryExpr) + if err != nil { + return err + } + + mustExist := generichelper.Contains(identifiers, "refreshed_at") && + generichelper.Contains(identifiers, "type") && + generichelper.Contains(identifiers, "service") + if !mustExist { + return fmt.Errorf("must exists these identifiers: refreshed_at, type. Current identifiers: %v", identifiers) + } + + return nil +} + // GetAll retrieves list of assets with filters func (r *AssetRepository) GetAll(ctx context.Context, flt asset.Filter) ([]asset.Asset, error) { builder := r.getAssetSQL().Offset(uint64(flt.Offset)) @@ -117,8 +137,41 @@ func (r *AssetRepository) GetCount(ctx context.Context, flt asset.Filter) (int, return total, nil } -// GetCountByQuery retrieves number of assets for every type based on query -func (r *AssetRepository) GetCountByQuery(ctx context.Context, sqlQuery string) (int, error) { +// GetCountByQueryExpr retrieves number of assets for every type based on query expr +func (r *AssetRepository) GetCountByQueryExpr(ctx context.Context, queryExpr string, isDeleteExpr bool) (int, error) { + var sqlQuery string + if isDeleteExpr { + deleteExpr := &DeleteAssetSQLExpr{ + queryexpr.SQLExpr{ + QueryExpr: queryExpr, + }, + } + query, err := queryexpr.ValidateAndGetQueryFromExpr(deleteExpr) + if err != nil { + return 0, err + } + sqlQuery = query + } else { + sqlExpr := &queryexpr.SQLExpr{ + QueryExpr: queryExpr, + } + query, err := queryexpr.ValidateAndGetQueryFromExpr(sqlExpr) + if err != nil { + return 0, err + } + sqlQuery = query + } + + total, err := r.getCountByQuery(ctx, sqlQuery) + if err != nil { + return 0, err + } + + return total, err +} + +// GetCountByQueryExpr retrieves number of assets for every type based on query expr +func (r *AssetRepository) getCountByQuery(ctx context.Context, sqlQuery string) (int, error) { builder := sq.Select("count(1)"). From("assets"). Where(sqlQuery) @@ -371,13 +424,23 @@ func (r *AssetRepository) DeleteByURN(ctx context.Context, urn string) error { return nil } -func (r *AssetRepository) DeleteByQuery(ctx context.Context, whereCondition string) ([]string, error) { +func (r *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) ([]string, error) { var allURNs []string err := r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { + deleteExpr := &DeleteAssetSQLExpr{ + queryexpr.SQLExpr{ + QueryExpr: queryExpr, + }, + } + query, err := queryexpr.ValidateAndGetQueryFromExpr(deleteExpr) + if err != nil { + return err + } + var lastID string for { // Fetch a batch of rows to delete using the last ID as a marker - urns, nextLastID, err := r.getAllURNsWithBatch(ctx, whereCondition, lastID) + urns, nextLastID, err := r.getAllURNsWithBatch(ctx, query, lastID) if err != nil { log.Printf("Failed to get batch to delete: %v", err) return err @@ -647,11 +710,7 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, onlyRefreshed := len(clog) == 1 && clog[0].Path[0] == "RefreshedAt" if onlyRefreshed { return r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { - if err := r.updateAsset(ctx, tx, assetID, newAsset); err != nil { - return err - } - - return nil + return r.updateAsset(ctx, tx, assetID, newAsset) }) } diff --git a/internal/workermanager/discovery_worker.go b/internal/workermanager/discovery_worker.go index 6a2c70a9..23abac36 100644 --- a/internal/workermanager/discovery_worker.go +++ b/internal/workermanager/discovery_worker.go @@ -15,7 +15,7 @@ import ( type DiscoveryRepository interface { Upsert(context.Context, asset.Asset) error DeleteByURN(ctx context.Context, assetURN string) error - DeleteByQuery(ctx context.Context, filterQuery string) error + DeleteByQueryExpr(ctx context.Context, queryExpr string) error SyncAssets(ctx context.Context, indexName string) (cleanupFn func() error, err error) } @@ -159,13 +159,13 @@ func (m *Manager) DeleteAsset(ctx context.Context, job worker.JobSpec) error { return nil } -func (m *Manager) EnqueueDeleteAssetsByQueryJob(ctx context.Context, filterQuery string) error { +func (m *Manager) EnqueueDeleteAssetsByQueryExprJob(ctx context.Context, queryExpr string) error { err := m.worker.Enqueue(ctx, worker.JobSpec{ Type: jobDeleteAssetsByQuery, - Payload: ([]byte)(filterQuery), + Payload: ([]byte)(queryExpr), }) if err != nil { - return fmt.Errorf("enqueue delete asset job: %w: urn '%s'", err, filterQuery) + return fmt.Errorf("enqueue delete asset job: %w: query expr '%s'", err, queryExpr) } return nil @@ -173,7 +173,7 @@ func (m *Manager) EnqueueDeleteAssetsByQueryJob(ctx context.Context, filterQuery func (m *Manager) deleteAssetsByQueryHandler() worker.JobHandler { return worker.JobHandler{ - Handle: m.DeleteAssetsByQuery, + Handle: m.DeleteAssetsByQueryExpr, JobOpts: worker.JobOptions{ MaxAttempts: 3, Timeout: m.indexTimeout, @@ -182,11 +182,11 @@ func (m *Manager) deleteAssetsByQueryHandler() worker.JobHandler { } } -func (m *Manager) DeleteAssetsByQuery(ctx context.Context, job worker.JobSpec) error { - filterQuery := (string)(job.Payload) - if err := m.discoveryRepo.DeleteByQuery(ctx, filterQuery); err != nil { +func (m *Manager) DeleteAssetsByQueryExpr(ctx context.Context, job worker.JobSpec) error { + queryExpr := (string)(job.Payload) + if err := m.discoveryRepo.DeleteByQueryExpr(ctx, queryExpr); err != nil { return &worker.RetryableError{ - Cause: fmt.Errorf("delete asset from discovery repo: %w: query '%s'", err, filterQuery), + Cause: fmt.Errorf("delete asset from discovery repo: %w: query expr '%s'", err, queryExpr), } } return nil diff --git a/internal/workermanager/in_situ_worker.go b/internal/workermanager/in_situ_worker.go index 0bb28e33..dfdca828 100644 --- a/internal/workermanager/in_situ_worker.go +++ b/internal/workermanager/in_situ_worker.go @@ -40,6 +40,13 @@ func (m *InSituWorker) EnqueueDeleteAssetJob(ctx context.Context, urn string) er return nil } +func (m *InSituWorker) EnqueueDeleteAssetsByQueryExprJob(ctx context.Context, queryExpr string) error { + if err := m.discoveryRepo.DeleteByQueryExpr(ctx, queryExpr); err != nil { + return fmt.Errorf("delete asset from discovery repo: %w: query expr '%s'", err, queryExpr) + } + return nil +} + func (m *InSituWorker) EnqueueSyncAssetJob(ctx context.Context, service string) error { const batchSize = 1000 diff --git a/pkg/generic_helper/generic_helper.go b/pkg/generic_helper/generic_helper.go index 559f2c45..178f1114 100644 --- a/pkg/generic_helper/generic_helper.go +++ b/pkg/generic_helper/generic_helper.go @@ -1,4 +1,4 @@ -package generic_helper +package generichelper // Contains checks if a target item exists in an array of any type. func Contains[T comparable](arr []T, target T) bool { diff --git a/pkg/query_expr/es_expr.go b/pkg/query_expr/es_expr.go new file mode 100644 index 00000000..cab7d9a6 --- /dev/null +++ b/pkg/query_expr/es_expr.go @@ -0,0 +1,219 @@ +package queryexpr + +import ( + "encoding/json" + "fmt" + "github.com/expr-lang/expr/ast" +) + +type ESExpr struct { + QueryExpr string + ESQuery map[string]interface{} +} + +// ToQuery default +func (e *ESExpr) ToQuery() (string, error) { + queryExprParsed, err := GetTreeNodeFromQueryExpr(e.QueryExpr) + if err != nil { + return "", err + } + + esQueryInterface := e.translateToEsQuery(queryExprParsed) + esQuery, ok := esQueryInterface.(map[string]interface{}) + if !ok { + return "", fmt.Errorf("failed to generate Elasticsearch query") + } + e.ESQuery = map[string]interface{}{"query": esQuery} + + // Convert to JSON + queryJSON, err := json.Marshal(e.ESQuery) + if err != nil { + return "", err + } + + return string(queryJSON), nil +} + +// Validate default: no validation +func (*ESExpr) Validate() error { + return nil +} + +// translateToEsQuery The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. +// TODO: implement translator for node type that still not covered right now. +func (e *ESExpr) translateToEsQuery(node *ast.Node) interface{} { + if *node == nil { + return nil + } + switch n := (*node).(type) { + case *ast.BinaryNode: + return e.translateBinaryNodeToEsQuery(n) + case *ast.NilNode: + return nil + case *ast.IdentifierNode: + return n.Value + case *ast.IntegerNode: + return n.Value + case *ast.FloatNode: + return n.Value + case *ast.BoolNode: + return n.Value + case *ast.StringNode: + return n.Value + case *ast.UnaryNode: + return e.translateUnaryNodeToEsQuery(n) + case *ast.ArrayNode: + return e.translateArrayNodeToEsQuery(n) + case *ast.ConstantNode: + return n.Value + case *ast.BuiltinNode: + result, err := GetQueryExprResult(n.String()) + if err != nil { + return nil + } + return result + case *ast.ConditionalNode: + result, err := GetQueryExprResult(n.String()) + if err != nil { + return nil + } + if nodeV, ok := result.(ast.Node); ok { + return e.translateToEsQuery(&nodeV) + } + } + + return nil +} + +func (e *ESExpr) translateBinaryNodeToEsQuery(n *ast.BinaryNode) map[string]interface{} { + left := e.translateToEsQuery(&n.Left) + right := e.translateToEsQuery(&n.Right) + + switch n.Operator { + case "&&": + return e.boolQuery("must", left, right) + case "||": + return e.boolQuery("should", left, right) + case "==": + return e.termQuery(left.(string), right) + case "!=": + return e.mustNotQuery(left.(string), right) + case "<", "<=", ">", ">=": + return e.rangeQuery(left.(string), e.operatorToEsQuery(n.Operator), right) + case "in": + return e.termsQuery(left.(string), right) + default: + return nil + } +} + +func (e *ESExpr) translateUnaryNodeToEsQuery(n *ast.UnaryNode) interface{} { + switch n.Operator { + case "not": + if binaryNode, ok := n.Node.(*ast.BinaryNode); ok && binaryNode.Operator == "in" { + left := e.translateToEsQuery(&binaryNode.Left) + right := e.translateToEsQuery(&binaryNode.Right) + return e.mustNotTermsQuery(left.(string), right) + } + return nil + case "!": + nodeValue := e.translateToEsQuery(&n.Node) + switch value := nodeValue.(type) { + case bool: + return !value + default: + return map[string]interface{}{ + "bool": map[string]interface{}{ + "must_not": []interface{}{nodeValue}, + }, + } + } + default: + return nil + } +} + +func (e *ESExpr) translateArrayNodeToEsQuery(n *ast.ArrayNode) []interface{} { + values := make([]interface{}, len(n.Nodes)) + for i, node := range n.Nodes { + values[i] = e.translateToEsQuery(&node) + } + return values +} + +func (*ESExpr) operatorToEsQuery(operator string) string { + switch operator { + case ">": + return "gt" + case ">=": + return "gte" + case "<": + return "lt" + case "<=": + return "lte" + } + + return operator +} + +func (*ESExpr) boolQuery(condition string, left, right interface{}) map[string]interface{} { + return map[string]interface{}{ + "bool": map[string]interface{}{ + condition: []interface{}{left, right}, + }, + } +} + +func (*ESExpr) termQuery(field string, value interface{}) map[string]interface{} { + return map[string]interface{}{ + "term": map[string]interface{}{ + field: value, + }, + } +} + +func (*ESExpr) mustNotQuery(field string, value interface{}) map[string]interface{} { + return map[string]interface{}{ + "bool": map[string]interface{}{ + "must_not": []interface{}{ + map[string]interface{}{ + "term": map[string]interface{}{ + field: value, + }, + }, + }, + }, + } +} + +func (*ESExpr) rangeQuery(field, operator string, value interface{}) map[string]interface{} { + return map[string]interface{}{ + "range": map[string]interface{}{ + field: map[string]interface{}{ + operator: value, + }, + }, + } +} + +func (*ESExpr) termsQuery(field string, values interface{}) map[string]interface{} { + return map[string]interface{}{ + "terms": map[string]interface{}{ + field: values, + }, + } +} + +func (*ESExpr) mustNotTermsQuery(field string, values interface{}) map[string]interface{} { + return map[string]interface{}{ + "bool": map[string]interface{}{ + "must_not": []interface{}{ + map[string]interface{}{ + "terms": map[string]interface{}{ + field: values, + }, + }, + }, + }, + } +} diff --git a/pkg/translator/test-json/equals-or-not-in-condition.json b/pkg/query_expr/es_test_data/equals-or-not-in-condition.json similarity index 100% rename from pkg/translator/test-json/equals-or-not-in-condition.json rename to pkg/query_expr/es_test_data/equals-or-not-in-condition.json diff --git a/pkg/translator/test-json/in-condition.json b/pkg/query_expr/es_test_data/in-condition.json similarity index 100% rename from pkg/translator/test-json/in-condition.json rename to pkg/query_expr/es_test_data/in-condition.json diff --git a/pkg/translator/test-json/lt-condition.json b/pkg/query_expr/es_test_data/lt-condition.json similarity index 100% rename from pkg/translator/test-json/lt-condition.json rename to pkg/query_expr/es_test_data/lt-condition.json diff --git a/pkg/query_expr/query_expr.go b/pkg/query_expr/query_expr.go new file mode 100644 index 00000000..cf85d2de --- /dev/null +++ b/pkg/query_expr/query_expr.go @@ -0,0 +1,71 @@ +package queryexpr + +import ( + "fmt" + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/ast" + "github.com/expr-lang/expr/parser" +) + +type ExprStr interface { + ToQuery() (string, error) + Validate() error +} + +type QueryExpr struct { + Identifiers []string +} + +type ExprParam map[string]interface{} + +func ValidateAndGetQueryFromExpr(exprStr ExprStr) (string, error) { + if err := exprStr.Validate(); err != nil { + return "", err + } + sqlQuery, err := exprStr.ToQuery() + if err != nil { + return "", err + } + + return sqlQuery, nil +} + +func (s *QueryExpr) Visit(node *ast.Node) { + if n, ok := (*node).(*ast.IdentifierNode); ok { + s.Identifiers = append(s.Identifiers, n.Value) + } +} + +func GetIdentifiers(queryExpr string) ([]string, error) { + queryExprParsed, err := GetTreeNodeFromQueryExpr(queryExpr) + if err != nil { + return nil, err + } + queryExprVisitor := &QueryExpr{} + ast.Walk(queryExprParsed, queryExprVisitor) + return queryExprVisitor.Identifiers, nil +} + +func GetTreeNodeFromQueryExpr(queryExpr string) (*ast.Node, error) { + parsed, err := parser.Parse(queryExpr) + if err != nil { + return nil, fmt.Errorf("error parsing expression: %w", err) + } + + return &parsed.Node, nil +} + +func GetQueryExprResult(fn string) (any, error) { + env := make(ExprParam) + compile, err := expr.Compile(fn) + if err != nil { + return nil, fmt.Errorf("failed to compile function '%s': %w", fn, err) + } + + result, err := expr.Run(compile, env) + if err != nil { + return nil, fmt.Errorf("failed to evaluate function '%s': %w", fn, err) + } + + return result, nil +} diff --git a/pkg/query_expr/sql_expr.go b/pkg/query_expr/sql_expr.go new file mode 100644 index 00000000..3f2ac6fa --- /dev/null +++ b/pkg/query_expr/sql_expr.go @@ -0,0 +1,130 @@ +package queryexpr + +import ( + "fmt" + "github.com/expr-lang/expr/ast" + "strconv" + "strings" +) + +type SQLExpr struct { + QueryExpr string + SQLQuery strings.Builder +} + +// ToQuery default +func (s *SQLExpr) ToQuery() (string, error) { + queryExprParsed, err := GetTreeNodeFromQueryExpr(s.QueryExpr) + if err != nil { + return "", err + } + s.ConvertToSQL(queryExprParsed) + return s.SQLQuery.String(), nil +} + +// Validate default: no validation +func (*SQLExpr) Validate() error { + return nil +} + +// ConvertToSQL The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. +// TODO: implement translator for node type that still not covered right now. +func (s *SQLExpr) ConvertToSQL(node *ast.Node) { + if *node == nil { + return + } + switch n := (*node).(type) { + case *ast.BinaryNode: + s.SQLQuery.WriteString("(") + s.ConvertToSQL(&n.Left) + + // write operator + operator := s.operatorToSQL(n) + s.SQLQuery.WriteString(fmt.Sprintf(" %s ", strings.ToUpper(operator))) + + s.ConvertToSQL(&n.Right) + s.SQLQuery.WriteString(")") + case *ast.NilNode: + s.SQLQuery.WriteString("NULL") + case *ast.IdentifierNode: + s.SQLQuery.WriteString(n.Value) + case *ast.IntegerNode: + s.SQLQuery.WriteString(strconv.FormatInt(int64(n.Value), 10)) + case *ast.FloatNode: + s.SQLQuery.WriteString(strconv.FormatFloat(n.Value, 'f', -1, 64)) + case *ast.BoolNode: + s.SQLQuery.WriteString(strconv.FormatBool(n.Value)) + case *ast.StringNode: + s.SQLQuery.WriteString(fmt.Sprintf("'%s'", n.Value)) + case *ast.ConstantNode: + s.SQLQuery.WriteString(fmt.Sprintf("%s", n.Value)) + case *ast.UnaryNode: + s.patchUnaryNode(n) + s.ConvertToSQL(&n.Node) + case *ast.BuiltinNode: + result, err := GetQueryExprResult(n.String()) + if err != nil { + return + } + s.SQLQuery.WriteString(fmt.Sprintf("%s", result)) + case *ast.ArrayNode: + s.SQLQuery.WriteString("(") + for i := range n.Nodes { + s.ConvertToSQL(&n.Nodes[i]) + if i != len(n.Nodes)-1 { + s.SQLQuery.WriteString(", ") + } + } + s.SQLQuery.WriteString(")") + case *ast.ConditionalNode: + result, err := GetQueryExprResult(n.String()) + if err != nil { + return + } + if nodeV, ok := result.(ast.Node); ok { + s.ConvertToSQL(&nodeV) + } + } +} + +func (*SQLExpr) patchUnaryNode(n *ast.UnaryNode) { + switch n.Operator { + case "not": + binaryNode, ok := (n.Node).(*ast.BinaryNode) + if ok && binaryNode.Operator == "in" { + ast.Patch(&n.Node, &ast.BinaryNode{ + Operator: "not in", + Left: binaryNode.Left, + Right: binaryNode.Right, + }) + } + case "!": + switch nodeV := n.Node.(type) { + case *ast.BoolNode: + ast.Patch(&n.Node, &ast.BoolNode{ + Value: !nodeV.Value, + }) + // TODO: adjust other types if needed + } + } +} + +func (*SQLExpr) operatorToSQL(bn *ast.BinaryNode) string { + switch { + case bn.Operator == "&&": + return "AND" + case bn.Operator == "||": + return "OR" + case bn.Operator == "!=": + if _, ok := bn.Right.(*ast.NilNode); ok { + return "IS NOT" + } + case bn.Operator == "==": + if _, ok := bn.Right.(*ast.NilNode); ok { + return "IS" + } + return "=" + } + + return bn.Operator +} diff --git a/pkg/translator/query_expr_translator.go b/pkg/translator/query_expr_translator.go deleted file mode 100644 index 2e06392d..00000000 --- a/pkg/translator/query_expr_translator.go +++ /dev/null @@ -1,380 +0,0 @@ -package translator - -import ( - "encoding/json" - "errors" - "fmt" - "github.com/expr-lang/expr" - "github.com/expr-lang/expr/ast" - "github.com/expr-lang/expr/parser" - "log" - "strconv" - "strings" -) - -type ExprParam map[string]interface{} - -type QueryExprTranslator struct { - QueryExpr string - SqlQuery strings.Builder - EsQuery map[string]interface{} - Identifiers []string -} - -func (q *QueryExprTranslator) Visit(node *ast.Node) { - if n, ok := (*node).(*ast.IdentifierNode); ok { - q.Identifiers = append(q.Identifiers, n.Value) - } -} - -func (q *QueryExprTranslator) GetIdentifiers() []string { - ast.Walk(q.getTreeNodeFromQueryExpr(), q) - return q.Identifiers -} - -func (q *QueryExprTranslator) getTreeNodeFromQueryExpr() *ast.Node { - parsed, err := parser.Parse(q.QueryExpr) - if err != nil { - log.Fatalf("Error parsing expression: %v", err) - } - - return &parsed.Node -} - -func (q *QueryExprTranslator) ConvertToSQL() (string, error) { - q.SqlQuery = strings.Builder{} - q.translateToSQL(q.getTreeNodeFromQueryExpr(), q) - return q.SqlQuery.String(), nil -} - -// translateToSQL The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. -// TODO: implement translator for node type that still not covered right now. -func (q *QueryExprTranslator) translateToSQL(node *ast.Node, translator *QueryExprTranslator) { - if *node == nil { - return - } - switch n := (*node).(type) { - case *ast.BinaryNode: - translator.SqlQuery.WriteString("(") - q.translateToSQL(&n.Left, translator) - - // write operator - operator := q.operatorToSQL(n) - translator.SqlQuery.WriteString(fmt.Sprintf(" %s ", strings.ToUpper(operator))) - - q.translateToSQL(&n.Right, translator) - translator.SqlQuery.WriteString(")") - case *ast.NilNode: - translator.SqlQuery.WriteString(fmt.Sprintf("%s", "NULL")) - case *ast.IdentifierNode: - translator.SqlQuery.WriteString(n.Value) - case *ast.IntegerNode: - translator.SqlQuery.WriteString(strconv.FormatInt(int64(n.Value), 10)) - case *ast.FloatNode: - translator.SqlQuery.WriteString(strconv.FormatFloat(n.Value, 'f', -1, 64)) - case *ast.BoolNode: - translator.SqlQuery.WriteString(strconv.FormatBool(n.Value)) - case *ast.StringNode: - translator.SqlQuery.WriteString(fmt.Sprintf("'%s'", n.Value)) - case *ast.ConstantNode: - translator.SqlQuery.WriteString(fmt.Sprintf("%s", n.Value)) - case *ast.UnaryNode: - switch n.Operator { - case "not": - binaryNode, ok := (n.Node).(*ast.BinaryNode) - if ok && binaryNode.Operator == "in" { - ast.Patch(&n.Node, &ast.BinaryNode{ - Operator: "not in", - Left: binaryNode.Left, - Right: binaryNode.Right, - }) - } - case "!": - switch nodeV := n.Node.(type) { - case *ast.BoolNode: - ast.Patch(&n.Node, &ast.BoolNode{ - Value: !nodeV.Value, - }) - // adjust other type if needed - } - } - q.translateToSQL(&n.Node, translator) - case *ast.BuiltinNode: - result, err := q.getQueryExprResult(n.String()) - if err != nil { - return - } - translator.SqlQuery.WriteString(fmt.Sprintf("%s", result)) - case *ast.ArrayNode: - translator.SqlQuery.WriteString("(") - for i := range n.Nodes { - q.translateToSQL(&n.Nodes[i], translator) - if i != len(n.Nodes)-1 { - translator.SqlQuery.WriteString(", ") - } - } - translator.SqlQuery.WriteString(")") - case *ast.ConditionalNode: - result, err := q.getQueryExprResult(n.String()) - if err != nil { - return - } - if nodeV, ok := result.(ast.Node); ok { - q.translateToSQL(&nodeV, translator) - } - case *ast.ChainNode: - case *ast.MemberNode: - case *ast.SliceNode: - case *ast.CallNode: - case *ast.ClosureNode: - case *ast.PointerNode: - case *ast.VariableDeclaratorNode: - case *ast.MapNode: - case *ast.PairNode: - default: - panic(fmt.Sprintf("undefined node type (%T)", node)) - } -} - -func (q *QueryExprTranslator) getQueryExprResult(fn string) (any, error) { - env := make(ExprParam) - compile, err := expr.Compile(fn) - if err != nil { - return nil, fmt.Errorf("failed to compile function '%s': %w", fn, err) - } - - result, err := expr.Run(compile, env) - if err != nil { - return nil, fmt.Errorf("failed to evaluate function '%s': %w", fn, err) - } - - return result, nil -} - -func (q *QueryExprTranslator) operatorToSQL(bn *ast.BinaryNode) string { - switch { - case bn.Operator == "&&": - return "AND" - case bn.Operator == "||": - return "OR" - case bn.Operator == "!=": - if _, ok := bn.Right.(*ast.NilNode); ok { - return "IS NOT" - } - case bn.Operator == "==": - if _, ok := bn.Right.(*ast.NilNode); ok { - return "IS" - } else { - return "=" - } - } - - return bn.Operator -} - -func (q *QueryExprTranslator) ConvertToEsQuery() (string, error) { - esQueryInterface := q.translateToEsQuery(q.getTreeNodeFromQueryExpr()) - esQuery, ok := esQueryInterface.(map[string]interface{}) - if !ok { - return "", errors.New("failed to generate Elasticsearch query") - } - q.EsQuery = map[string]interface{}{"query": esQuery} - - // Convert to JSON - queryJSON, err := json.Marshal(q.EsQuery) - if err != nil { - return "", err - } - - return string(queryJSON), nil -} - -// translateToEsQuery The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. -// TODO: implement translator for node type that still not covered right now. -func (q *QueryExprTranslator) translateToEsQuery(node *ast.Node) interface{} { - if *node == nil { - return nil - } - switch n := (*node).(type) { - case *ast.BinaryNode: - return q.translateBinaryNodeToEsQuery(n) - case *ast.NilNode: - return nil - case *ast.IdentifierNode: - return n.Value - case *ast.IntegerNode: - return n.Value - case *ast.FloatNode: - return n.Value - case *ast.BoolNode: - return n.Value - case *ast.StringNode: - return n.Value - case *ast.UnaryNode: - return q.translateUnaryNodeToEsQuery(n) - case *ast.ArrayNode: - return q.translateArrayNodeToEsQuery(n) - case *ast.ConstantNode: - return n.Value - case *ast.BuiltinNode: - result, err := q.getQueryExprResult(n.String()) - if err != nil { - return nil - } - return result - case *ast.ConditionalNode: - result, err := q.getQueryExprResult(n.String()) - if err != nil { - return nil - } - if nodeV, ok := result.(ast.Node); ok { - return q.translateToEsQuery(&nodeV) - } - case *ast.ChainNode: - case *ast.MemberNode: - case *ast.SliceNode: - case *ast.CallNode: - case *ast.ClosureNode: - case *ast.PointerNode: - case *ast.VariableDeclaratorNode: - case *ast.MapNode: - case *ast.PairNode: - default: - panic(fmt.Sprintf("undefined node type (%T)", node)) - } - - return nil -} - -func (q *QueryExprTranslator) translateBinaryNodeToEsQuery(n *ast.BinaryNode) map[string]interface{} { - left := q.translateToEsQuery(&n.Left) - right := q.translateToEsQuery(&n.Right) - - switch n.Operator { - case "&&": - return q.boolQuery("must", left, right) - case "||": - return q.boolQuery("should", left, right) - case "==": - return q.termQuery(left.(string), right) - case "!=": - return q.mustNotQuery(left.(string), right) - case "<", "<=", ">", ">=": - return q.rangeQuery(left.(string), q.operatorToEsQuery(n.Operator), right) - case "in": - return q.termsQuery(left.(string), right) - default: - return nil - } -} - -func (q *QueryExprTranslator) translateUnaryNodeToEsQuery(n *ast.UnaryNode) interface{} { - switch n.Operator { - case "not": - if binaryNode, ok := n.Node.(*ast.BinaryNode); ok && binaryNode.Operator == "in" { - left := q.translateToEsQuery(&binaryNode.Left) - right := q.translateToEsQuery(&binaryNode.Right) - return q.mustNotTermsQuery(left.(string), right) - } - return nil - case "!": - nodeValue := q.translateToEsQuery(&n.Node) - switch value := nodeValue.(type) { - case bool: - return !value - default: - return map[string]interface{}{ - "bool": map[string]interface{}{ - "must_not": []interface{}{nodeValue}, - }, - } - } - default: - return nil - } -} - -func (q *QueryExprTranslator) translateArrayNodeToEsQuery(n *ast.ArrayNode) []interface{} { - values := make([]interface{}, len(n.Nodes)) - for i, node := range n.Nodes { - values[i] = q.translateToEsQuery(&node) - } - return values -} - -func (q *QueryExprTranslator) operatorToEsQuery(operator string) string { - switch operator { - case ">": - return "gt" - case ">=": - return "gte" - case "<": - return "lt" - case "<=": - return "lte" - } - - return operator -} - -func (q *QueryExprTranslator) boolQuery(condition string, left, right interface{}) map[string]interface{} { - return map[string]interface{}{ - "bool": map[string]interface{}{ - condition: []interface{}{left, right}, - }, - } -} - -func (q *QueryExprTranslator) termQuery(field string, value interface{}) map[string]interface{} { - return map[string]interface{}{ - "term": map[string]interface{}{ - field: value, - }, - } -} - -func (q *QueryExprTranslator) mustNotQuery(field string, value interface{}) map[string]interface{} { - return map[string]interface{}{ - "bool": map[string]interface{}{ - "must_not": []interface{}{ - map[string]interface{}{ - "term": map[string]interface{}{ - field: value, - }, - }, - }, - }, - } -} - -func (q *QueryExprTranslator) rangeQuery(field, operator string, value interface{}) map[string]interface{} { - return map[string]interface{}{ - "range": map[string]interface{}{ - field: map[string]interface{}{ - operator: value, - }, - }, - } -} - -func (q *QueryExprTranslator) termsQuery(field string, values interface{}) map[string]interface{} { - return map[string]interface{}{ - "terms": map[string]interface{}{ - field: values, - }, - } -} - -func (q *QueryExprTranslator) mustNotTermsQuery(field string, values interface{}) map[string]interface{} { - return map[string]interface{}{ - "bool": map[string]interface{}{ - "must_not": []interface{}{ - map[string]interface{}{ - "terms": map[string]interface{}{ - field: values, - }, - }, - }, - }, - } -} diff --git a/pkg/translator/query_expr_translator_test.go b/pkg/translator/query_expr_translator_test.go deleted file mode 100644 index 23d329ed..00000000 --- a/pkg/translator/query_expr_translator_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package translator - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "reflect" - "testing" -) - -func TestQueryExprTranslator_ConvertToEsQuery(t *testing.T) { - tests := []struct { - name string - queryExprTranslator *QueryExprTranslator - want string - wantErr bool - }{ - { - name: "less than condition", - queryExprTranslator: &QueryExprTranslator{ - QueryExpr: `updated_at < "2024-04-05 23:59:59"`, - }, - want: "test-json/lt-condition.json", - wantErr: false, - }, - { - name: "in condition", - queryExprTranslator: &QueryExprTranslator{ - QueryExpr: `service in ["test1","test2","test3"]`, - }, - want: "test-json/in-condition.json", - wantErr: false, - }, - { - name: "equals or not in condition", - queryExprTranslator: &QueryExprTranslator{ - QueryExpr: `name == "John" || service not in ["test1","test2","test3"]`, - }, - want: "test-json/equals-or-not-in-condition.json", - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.queryExprTranslator.ConvertToEsQuery() - if (err != nil) != tt.wantErr { - t.Errorf("ConvertToEsQuery() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !deepEqual(tt.want, tt.queryExprTranslator.EsQuery) { - t.Errorf("ConvertToEsQuery() got = %v, want equal to json in file: %v", got, tt.want) - } - }) - } -} - -func TestQueryExprTranslator_ConvertToSQL(t *testing.T) { - tests := []struct { - name string - queryExprTranslator *QueryExprTranslator - want string - wantErr bool - }{ - { - name: "less than condition", - queryExprTranslator: &QueryExprTranslator{ - QueryExpr: `updated_at < "2024-04-05 23:59:59"`, - }, - want: `(updated_at < '2024-04-05 23:59:59')`, - wantErr: false, - }, - { - name: "in condition", - queryExprTranslator: &QueryExprTranslator{ - QueryExpr: `service in ["test1","test2","test3"]`, - }, - want: `(service IN ('test1', 'test2', 'test3'))`, - wantErr: false, - }, - { - name: "equals or not in condition", - queryExprTranslator: &QueryExprTranslator{ - QueryExpr: `name == "John" || service not in ["test1","test2","test3"]`, - }, - want: `((name = 'John') OR (service NOT IN ('test1', 'test2', 'test3')))`, - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.queryExprTranslator.ConvertToSQL() - if (err != nil) != tt.wantErr { - t.Errorf("ConvertToSQL() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("ConvertToSQL() got = %v, want %v", got, tt.want) - } - }) - } -} - -func deepEqual(jsonFileName string, result map[string]interface{}) bool { - // Step 1: Read the JSON file - fileContent, err := ioutil.ReadFile(jsonFileName) - if err != nil { - fmt.Println("Error reading the file:", err) - return false - } - - // Step 2: Unmarshal the file content into a Go data structure - var fileData map[string]interface{} - err = json.Unmarshal(fileContent, &fileData) - if err != nil { - fmt.Println("Error unmarshalling the file content:", err) - return false - } - - // Step 4: Compare the two Go data structures - if reflect.DeepEqual(fileData, result) { - return true - } else { - return false - } -} From 49bdac91e18cd22b76c15325f5810a37439ef314 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Fri, 9 Aug 2024 15:56:49 +0700 Subject: [PATCH 11/45] refactor: resolve all lint issues --- .../elasticsearch/discovery_repository.go | 4 ++-- internal/store/postgres/asset_repository.go | 4 ++-- pkg/query_expr/es_expr.go | 21 ++++++++++--------- pkg/query_expr/query_expr.go | 10 +++++---- pkg/query_expr/sql_expr.go | 19 +++++++++-------- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/internal/store/elasticsearch/discovery_repository.go b/internal/store/elasticsearch/discovery_repository.go index 09357c13..368ba779 100644 --- a/internal/store/elasticsearch/discovery_repository.go +++ b/internal/store/elasticsearch/discovery_repository.go @@ -6,14 +6,14 @@ import ( "encoding/json" "errors" "fmt" - generichelper "github.com/goto/compass/pkg/generic_helper" - queryexpr "github.com/goto/compass/pkg/query_expr" "io" "net/url" "strings" "time" "github.com/goto/compass/core/asset" + generichelper "github.com/goto/compass/pkg/generic_helper" + queryexpr "github.com/goto/compass/pkg/query_expr" "github.com/goto/salt/log" ) diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index 4c815d85..1ba93ca6 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -6,8 +6,6 @@ import ( "encoding/json" "errors" "fmt" - generichelper "github.com/goto/compass/pkg/generic_helper" - queryexpr "github.com/goto/compass/pkg/query_expr" "log" "strings" "time" @@ -15,6 +13,8 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/goto/compass/core/asset" "github.com/goto/compass/core/user" + generichelper "github.com/goto/compass/pkg/generic_helper" + queryexpr "github.com/goto/compass/pkg/query_expr" "github.com/jmoiron/sqlx" "github.com/r3labs/diff/v2" ) diff --git a/pkg/query_expr/es_expr.go b/pkg/query_expr/es_expr.go index cab7d9a6..e943941c 100644 --- a/pkg/query_expr/es_expr.go +++ b/pkg/query_expr/es_expr.go @@ -3,6 +3,7 @@ package queryexpr import ( "encoding/json" "fmt" + "github.com/expr-lang/expr/ast" ) @@ -41,11 +42,11 @@ func (*ESExpr) Validate() error { // translateToEsQuery The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. // TODO: implement translator for node type that still not covered right now. -func (e *ESExpr) translateToEsQuery(node *ast.Node) interface{} { - if *node == nil { +func (e *ESExpr) translateToEsQuery(node ast.Node) interface{} { + if node == nil { return nil } - switch n := (*node).(type) { + switch n := (node).(type) { case *ast.BinaryNode: return e.translateBinaryNodeToEsQuery(n) case *ast.NilNode: @@ -78,7 +79,7 @@ func (e *ESExpr) translateToEsQuery(node *ast.Node) interface{} { return nil } if nodeV, ok := result.(ast.Node); ok { - return e.translateToEsQuery(&nodeV) + return e.translateToEsQuery(nodeV) } } @@ -86,8 +87,8 @@ func (e *ESExpr) translateToEsQuery(node *ast.Node) interface{} { } func (e *ESExpr) translateBinaryNodeToEsQuery(n *ast.BinaryNode) map[string]interface{} { - left := e.translateToEsQuery(&n.Left) - right := e.translateToEsQuery(&n.Right) + left := e.translateToEsQuery(n.Left) + right := e.translateToEsQuery(n.Right) switch n.Operator { case "&&": @@ -111,13 +112,13 @@ func (e *ESExpr) translateUnaryNodeToEsQuery(n *ast.UnaryNode) interface{} { switch n.Operator { case "not": if binaryNode, ok := n.Node.(*ast.BinaryNode); ok && binaryNode.Operator == "in" { - left := e.translateToEsQuery(&binaryNode.Left) - right := e.translateToEsQuery(&binaryNode.Right) + left := e.translateToEsQuery(binaryNode.Left) + right := e.translateToEsQuery(binaryNode.Right) return e.mustNotTermsQuery(left.(string), right) } return nil case "!": - nodeValue := e.translateToEsQuery(&n.Node) + nodeValue := e.translateToEsQuery(n.Node) switch value := nodeValue.(type) { case bool: return !value @@ -136,7 +137,7 @@ func (e *ESExpr) translateUnaryNodeToEsQuery(n *ast.UnaryNode) interface{} { func (e *ESExpr) translateArrayNodeToEsQuery(n *ast.ArrayNode) []interface{} { values := make([]interface{}, len(n.Nodes)) for i, node := range n.Nodes { - values[i] = e.translateToEsQuery(&node) + values[i] = e.translateToEsQuery(node) } return values } diff --git a/pkg/query_expr/query_expr.go b/pkg/query_expr/query_expr.go index cf85d2de..3e7cac61 100644 --- a/pkg/query_expr/query_expr.go +++ b/pkg/query_expr/query_expr.go @@ -2,6 +2,7 @@ package queryexpr import ( "fmt" + "github.com/expr-lang/expr" "github.com/expr-lang/expr/ast" "github.com/expr-lang/expr/parser" @@ -30,7 +31,8 @@ func ValidateAndGetQueryFromExpr(exprStr ExprStr) (string, error) { return sqlQuery, nil } -func (s *QueryExpr) Visit(node *ast.Node) { +// Visit is implementation Visitor interface from expr-lang/expr lib, used by ast.Walk +func (s *QueryExpr) Visit(node *ast.Node) { //nolint:gocritic if n, ok := (*node).(*ast.IdentifierNode); ok { s.Identifiers = append(s.Identifiers, n.Value) } @@ -42,17 +44,17 @@ func GetIdentifiers(queryExpr string) ([]string, error) { return nil, err } queryExprVisitor := &QueryExpr{} - ast.Walk(queryExprParsed, queryExprVisitor) + ast.Walk(&queryExprParsed, queryExprVisitor) return queryExprVisitor.Identifiers, nil } -func GetTreeNodeFromQueryExpr(queryExpr string) (*ast.Node, error) { +func GetTreeNodeFromQueryExpr(queryExpr string) (ast.Node, error) { parsed, err := parser.Parse(queryExpr) if err != nil { return nil, fmt.Errorf("error parsing expression: %w", err) } - return &parsed.Node, nil + return parsed.Node, nil } func GetQueryExprResult(fn string) (any, error) { diff --git a/pkg/query_expr/sql_expr.go b/pkg/query_expr/sql_expr.go index 3f2ac6fa..efad4a7b 100644 --- a/pkg/query_expr/sql_expr.go +++ b/pkg/query_expr/sql_expr.go @@ -2,9 +2,10 @@ package queryexpr import ( "fmt" - "github.com/expr-lang/expr/ast" "strconv" "strings" + + "github.com/expr-lang/expr/ast" ) type SQLExpr struct { @@ -29,20 +30,20 @@ func (*SQLExpr) Validate() error { // ConvertToSQL The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. // TODO: implement translator for node type that still not covered right now. -func (s *SQLExpr) ConvertToSQL(node *ast.Node) { - if *node == nil { +func (s *SQLExpr) ConvertToSQL(node ast.Node) { + if node == nil { return } - switch n := (*node).(type) { + switch n := (node).(type) { case *ast.BinaryNode: s.SQLQuery.WriteString("(") - s.ConvertToSQL(&n.Left) + s.ConvertToSQL(n.Left) // write operator operator := s.operatorToSQL(n) s.SQLQuery.WriteString(fmt.Sprintf(" %s ", strings.ToUpper(operator))) - s.ConvertToSQL(&n.Right) + s.ConvertToSQL(n.Right) s.SQLQuery.WriteString(")") case *ast.NilNode: s.SQLQuery.WriteString("NULL") @@ -60,7 +61,7 @@ func (s *SQLExpr) ConvertToSQL(node *ast.Node) { s.SQLQuery.WriteString(fmt.Sprintf("%s", n.Value)) case *ast.UnaryNode: s.patchUnaryNode(n) - s.ConvertToSQL(&n.Node) + s.ConvertToSQL(n.Node) case *ast.BuiltinNode: result, err := GetQueryExprResult(n.String()) if err != nil { @@ -70,7 +71,7 @@ func (s *SQLExpr) ConvertToSQL(node *ast.Node) { case *ast.ArrayNode: s.SQLQuery.WriteString("(") for i := range n.Nodes { - s.ConvertToSQL(&n.Nodes[i]) + s.ConvertToSQL(n.Nodes[i]) if i != len(n.Nodes)-1 { s.SQLQuery.WriteString(", ") } @@ -82,7 +83,7 @@ func (s *SQLExpr) ConvertToSQL(node *ast.Node) { return } if nodeV, ok := result.(ast.Node); ok { - s.ConvertToSQL(&nodeV) + s.ConvertToSQL(nodeV) } } } From 3d412ebabfa52b7f990703001b44a7cc3a604397 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Fri, 9 Aug 2024 16:14:25 +0700 Subject: [PATCH 12/45] fix: add refreshed_at in insert asset query --- internal/store/postgres/asset_repository.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index 1ba93ca6..b660fd93 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -657,8 +657,8 @@ func (r *AssetRepository) insert(ctx context.Context, ast *asset.Asset) (string, ast.UpdatedAt = ast.CreatedAt ast.RefreshedAt = ast.CreatedAt query, args, err := sq.Insert("assets"). - Columns("urn", "type", "service", "name", "description", "data", "url", "labels", "created_at", "updated_by", "updated_at", "version"). - Values(ast.URN, ast.Type, ast.Service, ast.Name, ast.Description, ast.Data, ast.URL, ast.Labels, ast.CreatedAt, ast.UpdatedBy.ID, ast.UpdatedAt, asset.BaseVersion). + Columns("urn", "type", "service", "name", "description", "data", "url", "labels", "created_at", "updated_by", "updated_at", "refreshed_at", "version"). + Values(ast.URN, ast.Type, ast.Service, ast.Name, ast.Description, ast.Data, ast.URL, ast.Labels, ast.CreatedAt, ast.UpdatedBy.ID, ast.UpdatedAt, ast.RefreshedAt, asset.BaseVersion). Suffix("RETURNING \"id\""). PlaceholderFormat(sq.Dollar). ToSql() From c629b87f174d21e51c7460f35f5ee17a99778a76 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Fri, 9 Aug 2024 17:24:39 +0700 Subject: [PATCH 13/45] feat: add refreshed_at field in get all assets --- internal/store/postgres/asset_repository.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index b660fd93..de56c6f7 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -43,7 +43,8 @@ func (d *DeleteAssetSQLExpr) Validate() error { generichelper.Contains(identifiers, "type") && generichelper.Contains(identifiers, "service") if !mustExist { - return fmt.Errorf("must exists these identifiers: refreshed_at, type. Current identifiers: %v", identifiers) + return fmt.Errorf("must exists these identifiers: refreshed_at, type, and service. "+ + "Current identifiers: %v", identifiers) } return nil @@ -1003,6 +1004,7 @@ func (r *AssetRepository) getAssetSQL() sq.SelectBuilder { a.version as version, a.created_at as created_at, a.updated_at as updated_at, + a.refreshed_at as refreshed_at, u.id as "updated_by.id", u.uuid as "updated_by.uuid", u.email as "updated_by.email", From 41b32cb17fe07fcc7009aa158350b314982be317 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Fri, 9 Aug 2024 17:25:00 +0700 Subject: [PATCH 14/45] fix: update return error in delete assets --- internal/server/v1beta1/asset.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/internal/server/v1beta1/asset.go b/internal/server/v1beta1/asset.go index 0d5a7b0c..93c2acac 100644 --- a/internal/server/v1beta1/asset.go +++ b/internal/server/v1beta1/asset.go @@ -315,13 +315,7 @@ func (server *APIServer) DeleteAssets(ctx context.Context, req *compassv1beta1.D affectedRows, err := server.assetService.DeleteAssets(ctx, req.QueryExpr, req.DryRun) if err != nil { - if errors.As(err, new(asset.InvalidError)) { - return nil, status.Error(codes.InvalidArgument, err.Error()) - } - if errors.As(err, new(asset.NotFoundError)) { - return nil, status.Error(codes.NotFound, err.Error()) - } - return nil, internalServerError(server.logger, err.Error()) + return nil, status.Error(codes.InvalidArgument, err.Error()) } return &compassv1beta1.DeleteAssetsResponse{ From 3ef85597e37806682682f63887d2b7829ab7cc17 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Fri, 9 Aug 2024 18:54:50 +0700 Subject: [PATCH 15/45] fix: update asynchronous process in deletion assets --- core/asset/service.go | 47 ++++++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/core/asset/service.go b/core/asset/service.go index aee14979..78ff0987 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -7,6 +7,8 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "log" + "sync" "time" ) @@ -127,6 +129,10 @@ func (s *Service) DeleteAsset(ctx context.Context, id string) (err error) { } func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun bool) (affectedRows uint32, err error) { + var wg sync.WaitGroup + var urns []string + dbErrChan := make(chan error, 1) + total, err := s.assetRepository.GetCountByQueryExpr(ctx, queryExpr, true) if err != nil { return 0, err @@ -136,20 +142,37 @@ func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun boo return uint32(total), nil } + // Perform the Assets deletion asynchronously. + wg.Add(1) go func() { - urns, err := s.assetRepository.DeleteByQueryExpr(ctx, queryExpr) - if err != nil { - return - } - - if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(ctx, queryExpr); err != nil { - return - } + defer wg.Done() + urnsDeleted, err := s.assetRepository.DeleteByQueryExpr(context.Background(), queryExpr) + urns = urnsDeleted + dbErrChan <- err + close(dbErrChan) + }() - for _, urn := range urns { - if err := s.lineageRepository.DeleteByURN(ctx, urn); err != nil { - return - } + // Perform Elasticsearch and Lineage asynchronously if the Assets deletion is successful. + wg.Add(1) + go func() { + defer wg.Done() + dbErr := <-dbErrChan + if dbErr == nil { + go func() { + if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(context.Background(), queryExpr); err != nil { + log.Fatalf("Error occurred during Elasticsearch deletion: %s", err) + } + }() + + go func() { + for _, urn := range urns { + if err := s.lineageRepository.DeleteByURN(context.Background(), urn); err != nil { + log.Fatalf("Error occurred during Lineage deletion: %s", err) + } + } + }() + } else { + log.Printf("Database deletion failed, skipping Elasticsearch and Lineage deletions: %s", dbErr) } }() From 04d17a592653a1d5474d654c080bcfad9ad69f43 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Fri, 9 Aug 2024 18:56:25 +0700 Subject: [PATCH 16/45] refactor: fix lint issues --- core/asset/service.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/asset/service.go b/core/asset/service.go index 78ff0987..58e7e5c9 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -3,13 +3,14 @@ package asset import ( "context" "fmt" + "log" + "sync" + "time" + "github.com/google/uuid" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" - "log" - "sync" - "time" ) type Service struct { @@ -160,14 +161,14 @@ func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun boo if dbErr == nil { go func() { if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(context.Background(), queryExpr); err != nil { - log.Fatalf("Error occurred during Elasticsearch deletion: %s", err) + log.Printf("Error occurred during Elasticsearch deletion: %s", err) } }() go func() { for _, urn := range urns { if err := s.lineageRepository.DeleteByURN(context.Background(), urn); err != nil { - log.Fatalf("Error occurred during Lineage deletion: %s", err) + log.Printf("Error occurred during Lineage deletion: %s", err) } } }() From ac922dcc8df1b1157a49228c62512d1de0ae94e4 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Sat, 10 Aug 2024 17:04:34 +0700 Subject: [PATCH 17/45] feat: fix ConditionalNode logic and toString format --- pkg/query_expr/es_expr.go | 10 +--------- pkg/query_expr/sql_expr.go | 14 +++----------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/pkg/query_expr/es_expr.go b/pkg/query_expr/es_expr.go index e943941c..6c10c9b5 100644 --- a/pkg/query_expr/es_expr.go +++ b/pkg/query_expr/es_expr.go @@ -67,20 +67,12 @@ func (e *ESExpr) translateToEsQuery(node ast.Node) interface{} { return e.translateArrayNodeToEsQuery(n) case *ast.ConstantNode: return n.Value - case *ast.BuiltinNode: + case *ast.BuiltinNode, *ast.ConditionalNode: result, err := GetQueryExprResult(n.String()) if err != nil { return nil } return result - case *ast.ConditionalNode: - result, err := GetQueryExprResult(n.String()) - if err != nil { - return nil - } - if nodeV, ok := result.(ast.Node); ok { - return e.translateToEsQuery(nodeV) - } } return nil diff --git a/pkg/query_expr/sql_expr.go b/pkg/query_expr/sql_expr.go index efad4a7b..9b81ac09 100644 --- a/pkg/query_expr/sql_expr.go +++ b/pkg/query_expr/sql_expr.go @@ -58,16 +58,10 @@ func (s *SQLExpr) ConvertToSQL(node ast.Node) { case *ast.StringNode: s.SQLQuery.WriteString(fmt.Sprintf("'%s'", n.Value)) case *ast.ConstantNode: - s.SQLQuery.WriteString(fmt.Sprintf("%s", n.Value)) + s.SQLQuery.WriteString(fmt.Sprintf("%v", n.Value)) case *ast.UnaryNode: s.patchUnaryNode(n) s.ConvertToSQL(n.Node) - case *ast.BuiltinNode: - result, err := GetQueryExprResult(n.String()) - if err != nil { - return - } - s.SQLQuery.WriteString(fmt.Sprintf("%s", result)) case *ast.ArrayNode: s.SQLQuery.WriteString("(") for i := range n.Nodes { @@ -77,14 +71,12 @@ func (s *SQLExpr) ConvertToSQL(node ast.Node) { } } s.SQLQuery.WriteString(")") - case *ast.ConditionalNode: + case *ast.BuiltinNode, *ast.ConditionalNode: result, err := GetQueryExprResult(n.String()) if err != nil { return } - if nodeV, ok := result.(ast.Node); ok { - s.ConvertToSQL(nodeV) - } + s.SQLQuery.WriteString(fmt.Sprintf("%v", result)) } } From f2d5c8ad540b2683902e1815b088f7b9d1e45ba3 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Sun, 11 Aug 2024 02:13:40 +0700 Subject: [PATCH 18/45] refactor: remove redundant code, and make return error as soon as possible --- .../elasticsearch/discovery_repository.go | 6 +- internal/store/postgres/asset_repository.go | 16 +- pkg/query_expr/es_expr.go | 157 +++++++++++++----- pkg/query_expr/query_expr.go | 6 +- pkg/query_expr/sql_expr.go | 150 ++++++++++++----- 5 files changed, 229 insertions(+), 106 deletions(-) diff --git a/internal/store/elasticsearch/discovery_repository.go b/internal/store/elasticsearch/discovery_repository.go index 368ba779..9acb1ce2 100644 --- a/internal/store/elasticsearch/discovery_repository.go +++ b/internal/store/elasticsearch/discovery_repository.go @@ -31,7 +31,7 @@ type DeleteAssetESExpr struct { } func (d *DeleteAssetESExpr) Validate() error { - identifiers, err := queryexpr.GetIdentifiers(d.QueryExpr) + identifiers, err := queryexpr.GetIdentifiers(d.String()) if err != nil { return err } @@ -172,9 +172,7 @@ func (repo *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExp } deleteAssetESExpr := &DeleteAssetESExpr{ - queryexpr.ESExpr{ - QueryExpr: queryExpr, - }, + queryexpr.ESExpr(queryExpr), } esQuery, err := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) if err != nil { diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index de56c6f7..d1d50397 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -34,7 +34,7 @@ type DeleteAssetSQLExpr struct { } func (d *DeleteAssetSQLExpr) Validate() error { - identifiers, err := queryexpr.GetIdentifiers(d.QueryExpr) + identifiers, err := queryexpr.GetIdentifiers(d.String()) if err != nil { return err } @@ -143,9 +143,7 @@ func (r *AssetRepository) GetCountByQueryExpr(ctx context.Context, queryExpr str var sqlQuery string if isDeleteExpr { deleteExpr := &DeleteAssetSQLExpr{ - queryexpr.SQLExpr{ - QueryExpr: queryExpr, - }, + queryexpr.SQLExpr(queryExpr), } query, err := queryexpr.ValidateAndGetQueryFromExpr(deleteExpr) if err != nil { @@ -153,10 +151,8 @@ func (r *AssetRepository) GetCountByQueryExpr(ctx context.Context, queryExpr str } sqlQuery = query } else { - sqlExpr := &queryexpr.SQLExpr{ - QueryExpr: queryExpr, - } - query, err := queryexpr.ValidateAndGetQueryFromExpr(sqlExpr) + sqlExpr := queryexpr.SQLExpr(queryExpr) + query, err := queryexpr.ValidateAndGetQueryFromExpr(&sqlExpr) if err != nil { return 0, err } @@ -429,9 +425,7 @@ func (r *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr strin var allURNs []string err := r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { deleteExpr := &DeleteAssetSQLExpr{ - queryexpr.SQLExpr{ - QueryExpr: queryExpr, - }, + queryexpr.SQLExpr(queryExpr), } query, err := queryexpr.ValidateAndGetQueryFromExpr(deleteExpr) if err != nil { diff --git a/pkg/query_expr/es_expr.go b/pkg/query_expr/es_expr.go index 6c10c9b5..9199714d 100644 --- a/pkg/query_expr/es_expr.go +++ b/pkg/query_expr/es_expr.go @@ -7,27 +7,31 @@ import ( "github.com/expr-lang/expr/ast" ) -type ESExpr struct { - QueryExpr string - ESQuery map[string]interface{} +type ESExpr string + +func (e *ESExpr) String() string { + return string(*e) } // ToQuery default func (e *ESExpr) ToQuery() (string, error) { - queryExprParsed, err := GetTreeNodeFromQueryExpr(e.QueryExpr) + queryExprParsed, err := GetTreeNodeFromQueryExpr(e.String()) if err != nil { return "", err } - esQueryInterface := e.translateToEsQuery(queryExprParsed) + esQueryInterface, err := e.translateToEsQuery(queryExprParsed) + if err != nil { + return "", err + } esQuery, ok := esQueryInterface.(map[string]interface{}) if !ok { return "", fmt.Errorf("failed to generate Elasticsearch query") } - e.ESQuery = map[string]interface{}{"query": esQuery} + esQuery = map[string]interface{}{"query": esQuery} // Convert to JSON - queryJSON, err := json.Marshal(e.ESQuery) + queryJSON, err := json.Marshal(esQuery) if err != nil { return "", err } @@ -42,96 +46,155 @@ func (*ESExpr) Validate() error { // translateToEsQuery The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. // TODO: implement translator for node type that still not covered right now. -func (e *ESExpr) translateToEsQuery(node ast.Node) interface{} { +func (e *ESExpr) translateToEsQuery(node ast.Node) (interface{}, error) { if node == nil { - return nil + return nil, fmt.Errorf("cannot convert nil to Elasticsearch query") } switch n := (node).(type) { case *ast.BinaryNode: - return e.translateBinaryNodeToEsQuery(n) + return e.binaryNodeToEsQuery(n) case *ast.NilNode: - return nil + return nil, nil case *ast.IdentifierNode: - return n.Value + return n.Value, nil case *ast.IntegerNode: - return n.Value + return n.Value, nil case *ast.FloatNode: - return n.Value + return n.Value, nil case *ast.BoolNode: - return n.Value + return n.Value, nil case *ast.StringNode: - return n.Value + return n.Value, nil case *ast.UnaryNode: - return e.translateUnaryNodeToEsQuery(n) + return e.unaryNodeToEsQuery(n) case *ast.ArrayNode: - return e.translateArrayNodeToEsQuery(n) + return e.arrayNodeToEsQuery(n) case *ast.ConstantNode: - return n.Value + return n.Value, nil case *ast.BuiltinNode, *ast.ConditionalNode: result, err := GetQueryExprResult(n.String()) if err != nil { - return nil + return nil, err } - return result + return result, nil + default: + return nil, e.unsupportedQueryError(n) } - - return nil } -func (e *ESExpr) translateBinaryNodeToEsQuery(n *ast.BinaryNode) map[string]interface{} { - left := e.translateToEsQuery(n.Left) - right := e.translateToEsQuery(n.Right) +func (e *ESExpr) binaryNodeToEsQuery(n *ast.BinaryNode) (interface{}, error) { //nolint:gocognit + left, err := e.translateToEsQuery(n.Left) + if err != nil { + return nil, err + } + right, err := e.translateToEsQuery(n.Right) + if err != nil { + return nil, err + } switch n.Operator { case "&&": - return e.boolQuery("must", left, right) + return e.boolQuery("must", left, right), nil + case "||": - return e.boolQuery("should", left, right) + return e.boolQuery("should", left, right), nil + case "==": - return e.termQuery(left.(string), right) + if leftStr, ok := left.(string); ok { + return e.termQuery(leftStr, right), nil + } + result, err := GetQueryExprResult(n.String()) + if err != nil { + return nil, err + } + return result, nil + case "!=": - return e.mustNotQuery(left.(string), right) + if leftStr, ok := left.(string); ok { + return e.mustNotQuery(leftStr, right), nil + } + result, err := GetQueryExprResult(n.String()) + if err != nil { + return nil, err + } + return result, nil + case "<", "<=", ">", ">=": - return e.rangeQuery(left.(string), e.operatorToEsQuery(n.Operator), right) + if leftStr, ok := left.(string); ok { + return e.rangeQuery(leftStr, e.operatorToEsQuery(n.Operator), right), nil + } + result, err := GetQueryExprResult(n.String()) + if err != nil { + return nil, err + } + return result, nil + case "in": - return e.termsQuery(left.(string), right) + if leftStr, ok := left.(string); ok { + return e.termsQuery(leftStr, right), nil + } + result, err := GetQueryExprResult(n.String()) + if err != nil { + return nil, err + } + return result, nil + default: - return nil + result, err := GetQueryExprResult(n.String()) + if err != nil { + return nil, err + } + return result, nil } } -func (e *ESExpr) translateUnaryNodeToEsQuery(n *ast.UnaryNode) interface{} { +func (e *ESExpr) unaryNodeToEsQuery(n *ast.UnaryNode) (interface{}, error) { switch n.Operator { case "not": if binaryNode, ok := n.Node.(*ast.BinaryNode); ok && binaryNode.Operator == "in" { - left := e.translateToEsQuery(binaryNode.Left) - right := e.translateToEsQuery(binaryNode.Right) - return e.mustNotTermsQuery(left.(string), right) + left, err := e.translateToEsQuery(binaryNode.Left) + if err != nil { + return nil, err + } + right, err := e.translateToEsQuery(binaryNode.Right) + if err != nil { + return nil, err + } + return e.mustNotTermsQuery(left.(string), right), nil } - return nil + return nil, e.unsupportedQueryError(n) + case "!": - nodeValue := e.translateToEsQuery(n.Node) + nodeValue, err := e.translateToEsQuery(n.Node) + if err != nil { + return nil, err + } switch value := nodeValue.(type) { case bool: - return !value + return !value, nil default: return map[string]interface{}{ "bool": map[string]interface{}{ "must_not": []interface{}{nodeValue}, }, - } + }, nil } + default: - return nil + return nil, e.unsupportedQueryError(n) } } -func (e *ESExpr) translateArrayNodeToEsQuery(n *ast.ArrayNode) []interface{} { +func (e *ESExpr) arrayNodeToEsQuery(n *ast.ArrayNode) ([]interface{}, error) { values := make([]interface{}, len(n.Nodes)) for i, node := range n.Nodes { - values[i] = e.translateToEsQuery(node) + nodeValue, err := e.translateToEsQuery(node) + if err != nil { + return nil, err + } + values[i] = nodeValue } - return values + return values, nil } func (*ESExpr) operatorToEsQuery(operator string) string { @@ -210,3 +273,7 @@ func (*ESExpr) mustNotTermsQuery(field string, values interface{}) map[string]in }, } } + +func (*ESExpr) unsupportedQueryError(node ast.Node) error { + return fmt.Errorf("unsupported query expr: %s to Elasticsearch query", node.String()) +} diff --git a/pkg/query_expr/query_expr.go b/pkg/query_expr/query_expr.go index 3e7cac61..cbddcbfd 100644 --- a/pkg/query_expr/query_expr.go +++ b/pkg/query_expr/query_expr.go @@ -13,7 +13,7 @@ type ExprStr interface { Validate() error } -type QueryExpr struct { +type ExprVisitor struct { Identifiers []string } @@ -32,7 +32,7 @@ func ValidateAndGetQueryFromExpr(exprStr ExprStr) (string, error) { } // Visit is implementation Visitor interface from expr-lang/expr lib, used by ast.Walk -func (s *QueryExpr) Visit(node *ast.Node) { //nolint:gocritic +func (s *ExprVisitor) Visit(node *ast.Node) { //nolint:gocritic if n, ok := (*node).(*ast.IdentifierNode); ok { s.Identifiers = append(s.Identifiers, n.Value) } @@ -43,7 +43,7 @@ func GetIdentifiers(queryExpr string) ([]string, error) { if err != nil { return nil, err } - queryExprVisitor := &QueryExpr{} + queryExprVisitor := &ExprVisitor{} ast.Walk(&queryExprParsed, queryExprVisitor) return queryExprVisitor.Identifiers, nil } diff --git a/pkg/query_expr/sql_expr.go b/pkg/query_expr/sql_expr.go index 9b81ac09..9d081038 100644 --- a/pkg/query_expr/sql_expr.go +++ b/pkg/query_expr/sql_expr.go @@ -8,19 +8,23 @@ import ( "github.com/expr-lang/expr/ast" ) -type SQLExpr struct { - QueryExpr string - SQLQuery strings.Builder +type SQLExpr string + +func (s *SQLExpr) String() string { + return string(*s) } // ToQuery default func (s *SQLExpr) ToQuery() (string, error) { - queryExprParsed, err := GetTreeNodeFromQueryExpr(s.QueryExpr) + queryExprParsed, err := GetTreeNodeFromQueryExpr(s.String()) if err != nil { return "", err } - s.ConvertToSQL(queryExprParsed) - return s.SQLQuery.String(), nil + stringBuilder := &strings.Builder{} + if err := s.ConvertToSQL(queryExprParsed, stringBuilder); err != nil { + return "", err + } + return stringBuilder.String(), nil } // Validate default: no validation @@ -30,57 +34,96 @@ func (*SQLExpr) Validate() error { // ConvertToSQL The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. // TODO: implement translator for node type that still not covered right now. -func (s *SQLExpr) ConvertToSQL(node ast.Node) { +func (s *SQLExpr) ConvertToSQL(node ast.Node, stringBuilder *strings.Builder) error { if node == nil { - return + return fmt.Errorf("cannot convert nil to SQL query") } switch n := (node).(type) { case *ast.BinaryNode: - s.SQLQuery.WriteString("(") - s.ConvertToSQL(n.Left) - - // write operator - operator := s.operatorToSQL(n) - s.SQLQuery.WriteString(fmt.Sprintf(" %s ", strings.ToUpper(operator))) - - s.ConvertToSQL(n.Right) - s.SQLQuery.WriteString(")") + err := s.binaryNodeToSQLQuery(n, stringBuilder) + if err != nil { + return err + } case *ast.NilNode: - s.SQLQuery.WriteString("NULL") + stringBuilder.WriteString("NULL") case *ast.IdentifierNode: - s.SQLQuery.WriteString(n.Value) + stringBuilder.WriteString(n.Value) case *ast.IntegerNode: - s.SQLQuery.WriteString(strconv.FormatInt(int64(n.Value), 10)) + stringBuilder.WriteString(strconv.FormatInt(int64(n.Value), 10)) case *ast.FloatNode: - s.SQLQuery.WriteString(strconv.FormatFloat(n.Value, 'f', -1, 64)) + stringBuilder.WriteString(strconv.FormatFloat(n.Value, 'f', -1, 64)) case *ast.BoolNode: - s.SQLQuery.WriteString(strconv.FormatBool(n.Value)) + stringBuilder.WriteString(strconv.FormatBool(n.Value)) case *ast.StringNode: - s.SQLQuery.WriteString(fmt.Sprintf("'%s'", n.Value)) + fmt.Fprintf(stringBuilder, "'%s'", n.Value) case *ast.ConstantNode: - s.SQLQuery.WriteString(fmt.Sprintf("%v", n.Value)) + fmt.Fprintf(stringBuilder, "%v", n.Value) case *ast.UnaryNode: - s.patchUnaryNode(n) - s.ConvertToSQL(n.Node) + if err := s.patchUnaryNode(n); err != nil { + return err + } + if err := s.ConvertToSQL(n.Node, stringBuilder); err != nil { + return err + } case *ast.ArrayNode: - s.SQLQuery.WriteString("(") - for i := range n.Nodes { - s.ConvertToSQL(n.Nodes[i]) - if i != len(n.Nodes)-1 { - s.SQLQuery.WriteString(", ") - } + err := s.arrayNodeToSQLQuery(n, stringBuilder) + if err != nil { + return err } - s.SQLQuery.WriteString(")") case *ast.BuiltinNode, *ast.ConditionalNode: result, err := GetQueryExprResult(n.String()) if err != nil { - return + return err } - s.SQLQuery.WriteString(fmt.Sprintf("%v", result)) + fmt.Fprintf(stringBuilder, "%v", result) + default: + return s.unsupportedQueryError(n) } + + return nil } -func (*SQLExpr) patchUnaryNode(n *ast.UnaryNode) { +func (s *SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings.Builder) error { + operator := s.operatorToSQL(n) + if operator == "" { // most likely the node is operation + result, err := GetQueryExprResult(n.String()) + if err != nil { + return err + } + fmt.Fprintf(stringBuilder, "%v", result) + } else { + stringBuilder.WriteString("(") + if err := s.ConvertToSQL(n.Left, stringBuilder); err != nil { + return err + } + + // write operator + fmt.Fprintf(stringBuilder, " %s ", strings.ToUpper(operator)) + + if err := s.ConvertToSQL(n.Right, stringBuilder); err != nil { + return err + } + stringBuilder.WriteString(")") + } + + return nil +} + +func (s *SQLExpr) arrayNodeToSQLQuery(n *ast.ArrayNode, stringBuilder *strings.Builder) error { + stringBuilder.WriteString("(") + for i := range n.Nodes { + if err := s.ConvertToSQL(n.Nodes[i], stringBuilder); err != nil { + return err + } + if i != len(n.Nodes)-1 { + stringBuilder.WriteString(", ") + } + } + stringBuilder.WriteString(")") + return nil +} + +func (s *SQLExpr) patchUnaryNode(n *ast.UnaryNode) error { switch n.Operator { case "not": binaryNode, ok := (n.Node).(*ast.BinaryNode) @@ -90,6 +133,8 @@ func (*SQLExpr) patchUnaryNode(n *ast.UnaryNode) { Left: binaryNode.Left, Right: binaryNode.Right, }) + } else { + return s.unsupportedQueryError(n) } case "!": switch nodeV := n.Node.(type) { @@ -97,27 +142,46 @@ func (*SQLExpr) patchUnaryNode(n *ast.UnaryNode) { ast.Patch(&n.Node, &ast.BoolNode{ Value: !nodeV.Value, }) - // TODO: adjust other types if needed + default: + result, err := GetQueryExprResult(n.String()) + if err != nil { + return err + } + if boolResult, ok := result.(bool); ok { + ast.Patch(&n.Node, &ast.BoolNode{ + Value: !boolResult, + }) + return nil + } + return s.unsupportedQueryError(n) } } + + return nil } func (*SQLExpr) operatorToSQL(bn *ast.BinaryNode) string { - switch { - case bn.Operator == "&&": + switch bn.Operator { + case "&&": return "AND" - case bn.Operator == "||": + case "||": return "OR" - case bn.Operator == "!=": + case "!=": if _, ok := bn.Right.(*ast.NilNode); ok { return "IS NOT" } - case bn.Operator == "==": + case "==": if _, ok := bn.Right.(*ast.NilNode); ok { return "IS" } return "=" + case "<", "<=", ">", ">=": + return bn.Operator } - return bn.Operator + return "" // identify operation, like: +, -, *, etc +} + +func (*SQLExpr) unsupportedQueryError(node ast.Node) error { + return fmt.Errorf("unsupported query expr: %s to SQL query", node.String()) } From 13f53c99aa3dd6e50952dc0e120b77f73bdaa545 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Sun, 11 Aug 2024 12:24:57 +0700 Subject: [PATCH 19/45] feat: update mock using mockery --- core/asset/mocks/asset_repository.go | 109 ++++++++++++++++++ core/asset/mocks/discovery_repository.go | 43 +++++++ core/asset/mocks/worker_mock.go | 43 +++++++ .../server/v1beta1/mocks/asset_service.go | 54 +++++++++ .../mocks/discovery_repository_mock.go | 47 +++++++- 5 files changed, 294 insertions(+), 2 deletions(-) diff --git a/core/asset/mocks/asset_repository.go b/core/asset/mocks/asset_repository.go index c125b7b6..b48724b7 100644 --- a/core/asset/mocks/asset_repository.go +++ b/core/asset/mocks/asset_repository.go @@ -110,6 +110,61 @@ func (_c *AssetRepository_DeleteByID_Call) RunAndReturn(run func(context.Context return _c } +// DeleteByQueryExpr provides a mock function with given fields: ctx, queryExpr +func (_m *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) ([]string, error) { + ret := _m.Called(ctx, queryExpr) + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) ([]string, error)); ok { + return rf(ctx, queryExpr) + } + if rf, ok := ret.Get(0).(func(context.Context, string) []string); ok { + r0 = rf(ctx, queryExpr) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, queryExpr) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AssetRepository_DeleteByQueryExpr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteByQueryExpr' +type AssetRepository_DeleteByQueryExpr_Call struct { + *mock.Call +} + +// DeleteByQueryExpr is a helper method to define mock.On call +// - ctx context.Context +// - queryExpr string +func (_e *AssetRepository_Expecter) DeleteByQueryExpr(ctx interface{}, queryExpr interface{}) *AssetRepository_DeleteByQueryExpr_Call { + return &AssetRepository_DeleteByQueryExpr_Call{Call: _e.mock.On("DeleteByQueryExpr", ctx, queryExpr)} +} + +func (_c *AssetRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, queryExpr string)) *AssetRepository_DeleteByQueryExpr_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *AssetRepository_DeleteByQueryExpr_Call) Return(_a0 []string, _a1 error) *AssetRepository_DeleteByQueryExpr_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AssetRepository_DeleteByQueryExpr_Call) RunAndReturn(run func(context.Context, string) ([]string, error)) *AssetRepository_DeleteByQueryExpr_Call { + _c.Call.Return(run) + return _c +} + // DeleteByURN provides a mock function with given fields: ctx, urn func (_m *AssetRepository) DeleteByURN(ctx context.Context, urn string) error { ret := _m.Called(ctx, urn) @@ -475,6 +530,60 @@ func (_c *AssetRepository_GetCount_Call) RunAndReturn(run func(context.Context, return _c } +// GetCountByQueryExpr provides a mock function with given fields: _a0, _a1, _a2 +func (_m *AssetRepository) GetCountByQueryExpr(_a0 context.Context, _a1 string, _a2 bool) (int, error) { + ret := _m.Called(_a0, _a1, _a2) + + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, bool) (int, error)); ok { + return rf(_a0, _a1, _a2) + } + if rf, ok := ret.Get(0).(func(context.Context, string, bool) int); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Get(0).(int) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { + r1 = rf(_a0, _a1, _a2) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AssetRepository_GetCountByQueryExpr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCountByQueryExpr' +type AssetRepository_GetCountByQueryExpr_Call struct { + *mock.Call +} + +// GetCountByQueryExpr is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 string +// - _a2 bool +func (_e *AssetRepository_Expecter) GetCountByQueryExpr(_a0 interface{}, _a1 interface{}, _a2 interface{}) *AssetRepository_GetCountByQueryExpr_Call { + return &AssetRepository_GetCountByQueryExpr_Call{Call: _e.mock.On("GetCountByQueryExpr", _a0, _a1, _a2)} +} + +func (_c *AssetRepository_GetCountByQueryExpr_Call) Run(run func(_a0 context.Context, _a1 string, _a2 bool)) *AssetRepository_GetCountByQueryExpr_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(bool)) + }) + return _c +} + +func (_c *AssetRepository_GetCountByQueryExpr_Call) Return(_a0 int, _a1 error) *AssetRepository_GetCountByQueryExpr_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AssetRepository_GetCountByQueryExpr_Call) RunAndReturn(run func(context.Context, string, bool) (int, error)) *AssetRepository_GetCountByQueryExpr_Call { + _c.Call.Return(run) + return _c +} + // GetProbes provides a mock function with given fields: ctx, assetURN func (_m *AssetRepository) GetProbes(ctx context.Context, assetURN string) ([]asset.Probe, error) { ret := _m.Called(ctx, assetURN) diff --git a/core/asset/mocks/discovery_repository.go b/core/asset/mocks/discovery_repository.go index f9a56ca3..00627ed1 100644 --- a/core/asset/mocks/discovery_repository.go +++ b/core/asset/mocks/discovery_repository.go @@ -66,6 +66,49 @@ func (_c *DiscoveryRepository_DeleteByID_Call) RunAndReturn(run func(context.Con return _c } +// DeleteByQueryExpr provides a mock function with given fields: ctx, filterQuery +func (_m *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, filterQuery string) error { + ret := _m.Called(ctx, filterQuery) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, filterQuery) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DiscoveryRepository_DeleteByQueryExpr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteByQueryExpr' +type DiscoveryRepository_DeleteByQueryExpr_Call struct { + *mock.Call +} + +// DeleteByQueryExpr is a helper method to define mock.On call +// - ctx context.Context +// - filterQuery string +func (_e *DiscoveryRepository_Expecter) DeleteByQueryExpr(ctx interface{}, filterQuery interface{}) *DiscoveryRepository_DeleteByQueryExpr_Call { + return &DiscoveryRepository_DeleteByQueryExpr_Call{Call: _e.mock.On("DeleteByQueryExpr", ctx, filterQuery)} +} + +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, filterQuery string)) *DiscoveryRepository_DeleteByQueryExpr_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Return(_a0 error) *DiscoveryRepository_DeleteByQueryExpr_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) RunAndReturn(run func(context.Context, string) error) *DiscoveryRepository_DeleteByQueryExpr_Call { + _c.Call.Return(run) + return _c +} + // DeleteByURN provides a mock function with given fields: ctx, assetURN func (_m *DiscoveryRepository) DeleteByURN(ctx context.Context, assetURN string) error { ret := _m.Called(ctx, assetURN) diff --git a/core/asset/mocks/worker_mock.go b/core/asset/mocks/worker_mock.go index f3cd20c0..8807b64a 100644 --- a/core/asset/mocks/worker_mock.go +++ b/core/asset/mocks/worker_mock.go @@ -107,6 +107,49 @@ func (_c *Worker_EnqueueDeleteAssetJob_Call) RunAndReturn(run func(context.Conte return _c } +// EnqueueDeleteAssetsByQueryExprJob provides a mock function with given fields: ctx, queryExpr +func (_m *Worker) EnqueueDeleteAssetsByQueryExprJob(ctx context.Context, queryExpr string) error { + ret := _m.Called(ctx, queryExpr) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, queryExpr) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Worker_EnqueueDeleteAssetsByQueryExprJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnqueueDeleteAssetsByQueryExprJob' +type Worker_EnqueueDeleteAssetsByQueryExprJob_Call struct { + *mock.Call +} + +// EnqueueDeleteAssetsByQueryExprJob is a helper method to define mock.On call +// - ctx context.Context +// - queryExpr string +func (_e *Worker_Expecter) EnqueueDeleteAssetsByQueryExprJob(ctx interface{}, queryExpr interface{}) *Worker_EnqueueDeleteAssetsByQueryExprJob_Call { + return &Worker_EnqueueDeleteAssetsByQueryExprJob_Call{Call: _e.mock.On("EnqueueDeleteAssetsByQueryExprJob", ctx, queryExpr)} +} + +func (_c *Worker_EnqueueDeleteAssetsByQueryExprJob_Call) Run(run func(ctx context.Context, queryExpr string)) *Worker_EnqueueDeleteAssetsByQueryExprJob_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Worker_EnqueueDeleteAssetsByQueryExprJob_Call) Return(_a0 error) *Worker_EnqueueDeleteAssetsByQueryExprJob_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Worker_EnqueueDeleteAssetsByQueryExprJob_Call) RunAndReturn(run func(context.Context, string) error) *Worker_EnqueueDeleteAssetsByQueryExprJob_Call { + _c.Call.Return(run) + return _c +} + // EnqueueIndexAssetJob provides a mock function with given fields: ctx, ast func (_m *Worker) EnqueueIndexAssetJob(ctx context.Context, ast asset.Asset) error { ret := _m.Called(ctx, ast) diff --git a/internal/server/v1beta1/mocks/asset_service.go b/internal/server/v1beta1/mocks/asset_service.go index 1b185469..87121e2d 100644 --- a/internal/server/v1beta1/mocks/asset_service.go +++ b/internal/server/v1beta1/mocks/asset_service.go @@ -110,6 +110,60 @@ func (_c *AssetService_DeleteAsset_Call) RunAndReturn(run func(context.Context, return _c } +// DeleteAssets provides a mock function with given fields: ctx, queryExpr, dryRun +func (_m *AssetService) DeleteAssets(ctx context.Context, queryExpr string, dryRun bool) (uint32, error) { + ret := _m.Called(ctx, queryExpr, dryRun) + + var r0 uint32 + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, bool) (uint32, error)); ok { + return rf(ctx, queryExpr, dryRun) + } + if rf, ok := ret.Get(0).(func(context.Context, string, bool) uint32); ok { + r0 = rf(ctx, queryExpr, dryRun) + } else { + r0 = ret.Get(0).(uint32) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { + r1 = rf(ctx, queryExpr, dryRun) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// AssetService_DeleteAssets_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteAssets' +type AssetService_DeleteAssets_Call struct { + *mock.Call +} + +// DeleteAssets is a helper method to define mock.On call +// - ctx context.Context +// - queryExpr string +// - dryRun bool +func (_e *AssetService_Expecter) DeleteAssets(ctx interface{}, queryExpr interface{}, dryRun interface{}) *AssetService_DeleteAssets_Call { + return &AssetService_DeleteAssets_Call{Call: _e.mock.On("DeleteAssets", ctx, queryExpr, dryRun)} +} + +func (_c *AssetService_DeleteAssets_Call) Run(run func(ctx context.Context, queryExpr string, dryRun bool)) *AssetService_DeleteAssets_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(bool)) + }) + return _c +} + +func (_c *AssetService_DeleteAssets_Call) Return(_a0 uint32, _a1 error) *AssetService_DeleteAssets_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *AssetService_DeleteAssets_Call) RunAndReturn(run func(context.Context, string, bool) (uint32, error)) *AssetService_DeleteAssets_Call { + _c.Call.Return(run) + return _c +} + // GetAllAssets provides a mock function with given fields: ctx, flt, withTotal func (_m *AssetService) GetAllAssets(ctx context.Context, flt asset.Filter, withTotal bool) ([]asset.Asset, uint32, error) { ret := _m.Called(ctx, flt, withTotal) diff --git a/internal/workermanager/mocks/discovery_repository_mock.go b/internal/workermanager/mocks/discovery_repository_mock.go index 4fa2bb79..ca07cb45 100644 --- a/internal/workermanager/mocks/discovery_repository_mock.go +++ b/internal/workermanager/mocks/discovery_repository_mock.go @@ -23,6 +23,49 @@ func (_m *DiscoveryRepository) EXPECT() *DiscoveryRepository_Expecter { return &DiscoveryRepository_Expecter{mock: &_m.Mock} } +// DeleteByQueryExpr provides a mock function with given fields: ctx, queryExpr +func (_m *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) error { + ret := _m.Called(ctx, queryExpr) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, queryExpr) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DiscoveryRepository_DeleteByQueryExpr_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteByQueryExpr' +type DiscoveryRepository_DeleteByQueryExpr_Call struct { + *mock.Call +} + +// DeleteByQueryExpr is a helper method to define mock.On call +// - ctx context.Context +// - queryExpr string +func (_e *DiscoveryRepository_Expecter) DeleteByQueryExpr(ctx interface{}, queryExpr interface{}) *DiscoveryRepository_DeleteByQueryExpr_Call { + return &DiscoveryRepository_DeleteByQueryExpr_Call{Call: _e.mock.On("DeleteByQueryExpr", ctx, queryExpr)} +} + +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, queryExpr string)) *DiscoveryRepository_DeleteByQueryExpr_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Return(_a0 error) *DiscoveryRepository_DeleteByQueryExpr_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) RunAndReturn(run func(context.Context, string) error) *DiscoveryRepository_DeleteByQueryExpr_Call { + _c.Call.Return(run) + return _c +} + // DeleteByURN provides a mock function with given fields: ctx, assetURN func (_m *DiscoveryRepository) DeleteByURN(ctx context.Context, assetURN string) error { ret := _m.Called(ctx, assetURN) @@ -111,8 +154,8 @@ func (_c *DiscoveryRepository_SyncAssets_Call) Run(run func(ctx context.Context, return _c } -func (_c *DiscoveryRepository_SyncAssets_Call) Return(cleanup func() error, err error) *DiscoveryRepository_SyncAssets_Call { - _c.Call.Return(cleanup, err) +func (_c *DiscoveryRepository_SyncAssets_Call) Return(cleanupFn func() error, err error) *DiscoveryRepository_SyncAssets_Call { + _c.Call.Return(cleanupFn, err) return _c } From 5411c6e1730efe9c118a937dc1f6f96c271bfe5a Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Sun, 11 Aug 2024 13:25:30 +0700 Subject: [PATCH 20/45] feat: equalize current time at created_at and updated_at based on refreshed_at both in postgresql and elasticsearch --- internal/store/postgres/asset_repository.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index d1d50397..1ed2660f 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -648,9 +648,8 @@ func (r *AssetRepository) deleteWithPredicate(ctx context.Context, pred sq.Eq) ( func (r *AssetRepository) insert(ctx context.Context, ast *asset.Asset) (string, error) { var id string err := r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { - ast.CreatedAt = time.Now() + ast.CreatedAt = ast.RefreshedAt // current time ast.UpdatedAt = ast.CreatedAt - ast.RefreshedAt = ast.CreatedAt query, args, err := sq.Insert("assets"). Columns("urn", "type", "service", "name", "description", "data", "url", "labels", "created_at", "updated_by", "updated_at", "refreshed_at", "version"). Values(ast.URN, ast.Type, ast.Service, ast.Name, ast.Description, ast.Data, ast.URL, ast.Labels, ast.CreatedAt, ast.UpdatedBy.ID, ast.UpdatedAt, ast.RefreshedAt, asset.BaseVersion). @@ -717,7 +716,7 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, } newAsset.Version = newVersion newAsset.ID = oldAsset.ID - newAsset.UpdatedAt = time.Now() + newAsset.UpdatedAt = newAsset.RefreshedAt // current time if err := r.updateAsset(ctx, tx, assetID, newAsset); err != nil { return err From 7092fef1e133ddcc0da34ea279a7feebf30e3544 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Sun, 11 Aug 2024 15:00:46 +0700 Subject: [PATCH 21/45] refactor: rename var --- internal/store/postgres/asset_repository.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index 1ed2660f..1e4c69ac 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -701,8 +701,8 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, return nil } - onlyRefreshed := len(clog) == 1 && clog[0].Path[0] == "RefreshedAt" - if onlyRefreshed { + refreshedOnly := len(clog) == 1 && clog[0].Path[0] == "RefreshedAt" + if refreshedOnly { return r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { return r.updateAsset(ctx, tx, assetID, newAsset) }) From 22d6d9e96c78c000155a1538e5e6b4fb0ec6b333 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Sun, 11 Aug 2024 15:38:26 +0700 Subject: [PATCH 22/45] refactor: remove unused go module --- go.mod | 1 - go.sum | 3 --- 2 files changed, 4 deletions(-) diff --git a/go.mod b/go.mod index 8deaea14..35261187 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/goto/compass go 1.20 require ( - github.com/DataDog/datadog-go/v5 v5.1.1 github.com/MakeNowJust/heredoc v1.0.0 github.com/Masterminds/semver/v3 v3.1.1 github.com/Masterminds/squirrel v1.5.2 diff --git a/go.sum b/go.sum index 9290d1be..1adf32e2 100644 --- a/go.sum +++ b/go.sum @@ -93,8 +93,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= -github.com/DataDog/datadog-go/v5 v5.1.1 h1:JLZ6s2K1pG2h9GkvEvMdEGqMDyVLEAccdX5TltWcLMU= -github.com/DataDog/datadog-go/v5 v5.1.1/go.mod h1:KhiYb2Badlv9/rofz+OznKoEF5XKTonWyhx5K83AP8E= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= @@ -109,7 +107,6 @@ github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugX github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= From 5994448655eabc7abc95955beef8d9fa344dfb78 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Sun, 11 Aug 2024 15:52:21 +0700 Subject: [PATCH 23/45] test: fix existing unit tests --- core/asset/service_test.go | 11 ++++++----- internal/workermanager/discovery_worker.go | 8 ++++---- internal/workermanager/job_types.go | 8 ++++---- internal/workermanager/worker_manager.go | 8 ++++---- internal/workermanager/worker_manager_test.go | 9 ++++++--- 5 files changed, 24 insertions(+), 20 deletions(-) diff --git a/core/asset/service_test.go b/core/asset/service_test.go index db01ed0d..82771bc7 100644 --- a/core/asset/service_test.go +++ b/core/asset/service_test.go @@ -11,6 +11,7 @@ import ( "github.com/goto/compass/core/asset/mocks" "github.com/goto/compass/internal/workermanager" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestService_GetAllAssets(t *testing.T) { @@ -195,7 +196,7 @@ func TestService_UpsertAsset(t *testing.T) { Asset: sampleAsset, Setup: func(ctx context.Context, ar *mocks.AssetRepository, dr *mocks.DiscoveryRepository, lr *mocks.LineageRepository) { ar.EXPECT().Upsert(ctx, sampleAsset).Return(sampleAsset.ID, nil) - dr.EXPECT().Upsert(ctx, *sampleAsset).Return(errors.New("unknown error")) + dr.EXPECT().Upsert(ctx, mock.AnythingOfType("asset.Asset")).Return(errors.New("unknown error")) }, Err: errors.New("unknown error"), ReturnedID: sampleAsset.ID, @@ -207,7 +208,7 @@ func TestService_UpsertAsset(t *testing.T) { Downstreams: sampleNodes2, Setup: func(ctx context.Context, ar *mocks.AssetRepository, dr *mocks.DiscoveryRepository, lr *mocks.LineageRepository) { ar.EXPECT().Upsert(ctx, sampleAsset).Return(sampleAsset.ID, nil) - dr.EXPECT().Upsert(ctx, *sampleAsset).Return(nil) + dr.EXPECT().Upsert(ctx, mock.AnythingOfType("asset.Asset")).Return(nil) lr.EXPECT().Upsert(ctx, sampleAsset.URN, sampleNodes1, sampleNodes2).Return(errors.New("unknown error")) }, Err: errors.New("unknown error"), @@ -220,7 +221,7 @@ func TestService_UpsertAsset(t *testing.T) { Downstreams: sampleNodes2, Setup: func(ctx context.Context, ar *mocks.AssetRepository, dr *mocks.DiscoveryRepository, lr *mocks.LineageRepository) { ar.EXPECT().Upsert(ctx, sampleAsset).Return(sampleAsset.ID, nil) - dr.EXPECT().Upsert(ctx, *sampleAsset).Return(nil) + dr.EXPECT().Upsert(ctx, mock.AnythingOfType("asset.Asset")).Return(nil) lr.EXPECT().Upsert(ctx, sampleAsset.URN, sampleNodes1, sampleNodes2).Return(nil) }, Err: nil, @@ -278,7 +279,7 @@ func TestService_UpsertAssetWithoutLineage(t *testing.T) { Asset: sampleAsset, Setup: func(ctx context.Context, ar *mocks.AssetRepository, dr *mocks.DiscoveryRepository) { ar.EXPECT().Upsert(ctx, sampleAsset).Return(sampleAsset.ID, nil) - dr.EXPECT().Upsert(ctx, *sampleAsset).Return(errors.New("unknown error")) + dr.EXPECT().Upsert(ctx, mock.AnythingOfType("asset.Asset")).Return(errors.New("unknown error")) }, Err: errors.New("unknown error"), }, @@ -287,7 +288,7 @@ func TestService_UpsertAssetWithoutLineage(t *testing.T) { Asset: sampleAsset, Setup: func(ctx context.Context, ar *mocks.AssetRepository, dr *mocks.DiscoveryRepository) { ar.EXPECT().Upsert(ctx, sampleAsset).Return(sampleAsset.ID, nil) - dr.EXPECT().Upsert(ctx, *sampleAsset).Return(nil) + dr.EXPECT().Upsert(ctx, mock.AnythingOfType("asset.Asset")).Return(nil) }, ReturnedID: sampleAsset.ID, }, diff --git a/internal/workermanager/discovery_worker.go b/internal/workermanager/discovery_worker.go index 23abac36..add8fe67 100644 --- a/internal/workermanager/discovery_worker.go +++ b/internal/workermanager/discovery_worker.go @@ -26,7 +26,7 @@ func (m *Manager) EnqueueIndexAssetJob(ctx context.Context, ast asset.Asset) err } err = m.worker.Enqueue(ctx, worker.JobSpec{ - Type: jobIndexAsset, + Type: JobIndexAsset, Payload: payload, }) if err != nil { @@ -128,7 +128,7 @@ func (m *Manager) SyncAssets(ctx context.Context, job worker.JobSpec) error { func (m *Manager) EnqueueDeleteAssetJob(ctx context.Context, urn string) error { err := m.worker.Enqueue(ctx, worker.JobSpec{ - Type: jobDeleteAsset, + Type: JobDeleteAsset, Payload: ([]byte)(urn), }) if err != nil { @@ -161,7 +161,7 @@ func (m *Manager) DeleteAsset(ctx context.Context, job worker.JobSpec) error { func (m *Manager) EnqueueDeleteAssetsByQueryExprJob(ctx context.Context, queryExpr string) error { err := m.worker.Enqueue(ctx, worker.JobSpec{ - Type: jobDeleteAssetsByQuery, + Type: JobDeleteAssetsByQuery, Payload: ([]byte)(queryExpr), }) if err != nil { @@ -194,7 +194,7 @@ func (m *Manager) DeleteAssetsByQueryExpr(ctx context.Context, job worker.JobSpe func (m *Manager) EnqueueSyncAssetJob(ctx context.Context, service string) error { err := m.worker.Enqueue(ctx, worker.JobSpec{ - Type: jobSyncAsset, + Type: JobSyncAsset, Payload: ([]byte)(service), }) if err != nil { diff --git a/internal/workermanager/job_types.go b/internal/workermanager/job_types.go index 7b2849b6..3e34ddf1 100644 --- a/internal/workermanager/job_types.go +++ b/internal/workermanager/job_types.go @@ -1,8 +1,8 @@ package workermanager const ( - jobIndexAsset = "index-asset" - jobDeleteAsset = "delete-asset" - jobDeleteAssetsByQuery = "delete-assets-by-query" - jobSyncAsset = "sync-asset" + JobIndexAsset = "index-asset" + JobDeleteAsset = "delete-asset" + JobDeleteAssetsByQuery = "delete-assets-by-query" + JobSyncAsset = "sync-asset" ) diff --git a/internal/workermanager/worker_manager.go b/internal/workermanager/worker_manager.go index ed795e52..91559ddd 100644 --- a/internal/workermanager/worker_manager.go +++ b/internal/workermanager/worker_manager.go @@ -124,10 +124,10 @@ func (m *Manager) init() error { m.initDone.Store(true) jobHandlers := map[string]worker.JobHandler{ - jobIndexAsset: m.indexAssetHandler(), - jobDeleteAsset: m.deleteAssetHandler(), - jobDeleteAssetsByQuery: m.deleteAssetsByQueryHandler(), - jobSyncAsset: m.syncAssetHandler(), + JobIndexAsset: m.indexAssetHandler(), + JobDeleteAsset: m.deleteAssetHandler(), + JobDeleteAssetsByQuery: m.deleteAssetsByQueryHandler(), + JobSyncAsset: m.syncAssetHandler(), } for typ, h := range jobHandlers { if err := m.worker.Register(typ, h); err != nil { diff --git a/internal/workermanager/worker_manager_test.go b/internal/workermanager/worker_manager_test.go index e5cbef07..41b30cae 100644 --- a/internal/workermanager/worker_manager_test.go +++ b/internal/workermanager/worker_manager_test.go @@ -77,13 +77,16 @@ func TestManager_Run(t *testing.T) { wrkr := mocks.NewWorker(t) mgr := workermanager.NewWithWorker(wrkr, workermanager.Deps{}) wrkr.EXPECT(). - Register("index-asset", mock.AnythingOfType("worker.JobHandler")). + Register(workermanager.JobIndexAsset, mock.AnythingOfType("worker.JobHandler")). Return(nil) wrkr.EXPECT(). - Register("delete-asset", mock.AnythingOfType("worker.JobHandler")). + Register(workermanager.JobDeleteAsset, mock.AnythingOfType("worker.JobHandler")). Return(nil) wrkr.EXPECT(). - Register("sync-asset", mock.AnythingOfType("worker.JobHandler")). + Register(workermanager.JobDeleteAssetsByQuery, mock.AnythingOfType("worker.JobHandler")). + Return(nil) + wrkr.EXPECT(). + Register(workermanager.JobSyncAsset, mock.AnythingOfType("worker.JobHandler")). Return(nil) wrkr.EXPECT(). Run(ctx). From 2eee272006089ce0db30e01d1fbef9dd69e3d4da Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Sun, 11 Aug 2024 16:54:21 +0700 Subject: [PATCH 24/45] test: fix existing unit tests --- internal/store/postgres/asset_repository_test.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/store/postgres/asset_repository_test.go b/internal/store/postgres/asset_repository_test.go index 059c2113..13059c47 100644 --- a/internal/store/postgres/asset_repository_test.go +++ b/internal/store/postgres/asset_repository_test.go @@ -977,11 +977,12 @@ func (r *AssetRepositoryTestSuite) TestUpsert() { r.Run("on insert", func() { r.Run("set ID to asset and version to base version", func() { ast := asset.Asset{ - URN: "urn-u-1", - Type: "table", - Service: "bigquery", - URL: "https://sample-url.com", - UpdatedBy: r.users[0], + URN: "urn-u-1", + Type: "table", + Service: "bigquery", + URL: "https://sample-url.com", + UpdatedBy: r.users[0], + RefreshedAt: time.Now(), } id, err := r.repository.Upsert(r.ctx, &ast) r.Equal(asset.BaseVersion, ast.Version) @@ -998,6 +999,7 @@ func (r *AssetRepositoryTestSuite) TestUpsert() { r.assertAsset(&ast, &assetInDB) ast2 := ast + ast2.RefreshedAt = time.Now() ast2.Description = "create a new version" // to force fetch from asset_versions. _, err = r.repository.Upsert(r.ctx, &ast2) r.NoError(err) @@ -1915,11 +1917,13 @@ func (r *AssetRepositoryTestSuite) assertAsset(expectedAsset, actualAsset *asset // sanitize time to make the assets comparable expectedAsset.CreatedAt = time.Time{} expectedAsset.UpdatedAt = time.Time{} + expectedAsset.RefreshedAt = time.Time{} expectedAsset.UpdatedBy.CreatedAt = time.Time{} expectedAsset.UpdatedBy.UpdatedAt = time.Time{} actualAsset.CreatedAt = time.Time{} actualAsset.UpdatedAt = time.Time{} + actualAsset.RefreshedAt = time.Time{} actualAsset.UpdatedBy.CreatedAt = time.Time{} actualAsset.UpdatedBy.UpdatedAt = time.Time{} From a08f729743c34ce9b523171789ab6901fac22029 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Mon, 12 Aug 2024 11:03:55 +0700 Subject: [PATCH 25/45] refactor: makes codes to more readable --- core/asset/service.go | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/core/asset/service.go b/core/asset/service.go index 58e7e5c9..fdae54d8 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -159,19 +159,8 @@ func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun boo defer wg.Done() dbErr := <-dbErrChan if dbErr == nil { - go func() { - if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(context.Background(), queryExpr); err != nil { - log.Printf("Error occurred during Elasticsearch deletion: %s", err) - } - }() - - go func() { - for _, urn := range urns { - if err := s.lineageRepository.DeleteByURN(context.Background(), urn); err != nil { - log.Printf("Error occurred during Lineage deletion: %s", err) - } - } - }() + s.deleteAssetsInElasticsearchAsynchronously(queryExpr) + s.deleteAssetsInLineageAsynchronously(urns) } else { log.Printf("Database deletion failed, skipping Elasticsearch and Lineage deletions: %s", dbErr) } @@ -180,6 +169,24 @@ func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun boo return uint32(total), nil } +func (s *Service) deleteAssetsInElasticsearchAsynchronously(queryExpr string) { + go func() { + if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(context.Background(), queryExpr); err != nil { + log.Printf("Error occurred during Elasticsearch deletion: %s", err) + } + }() +} + +func (s *Service) deleteAssetsInLineageAsynchronously(urns []string) { + go func() { + for _, urn := range urns { + if err := s.lineageRepository.DeleteByURN(context.Background(), urn); err != nil { + log.Printf("Error occurred during Lineage deletion: %s", err) + } + } + }() +} + func (s *Service) GetAssetByID(ctx context.Context, id string) (Asset, error) { ast, err := s.assetByIDWithoutProbes(ctx, "GetAssetByID", id) if err != nil { From a9267921f01f8f637d32c1c107ac241cd3760aeb Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Mon, 12 Aug 2024 16:56:32 +0700 Subject: [PATCH 26/45] test: create unit test for deletion API --- core/asset/service.go | 38 +++---- core/asset/service_test.go | 74 ++++++++++++ internal/server/v1beta1/asset_test.go | 91 ++++++++++++++- .../discovery_repository_test.go | 106 ++++++++++++++++++ internal/store/postgres/asset_repository.go | 2 +- .../store/postgres/asset_repository_test.go | 46 ++++++++ internal/workermanager/discovery_worker.go | 4 +- .../workermanager/discovery_worker_test.go | 89 ++++++++++++++- internal/workermanager/in_situ_worker.go | 2 +- internal/workermanager/in_situ_worker_test.go | 39 +++++++ 10 files changed, 463 insertions(+), 28 deletions(-) diff --git a/core/asset/service.go b/core/asset/service.go index fdae54d8..cfffb359 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -143,7 +143,13 @@ func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun boo return uint32(total), nil } - // Perform the Assets deletion asynchronously. + s.deleteAssetsAsynchronously(&wg, queryExpr, urns, dbErrChan) + + return uint32(total), nil +} + +func (s *Service) deleteAssetsAsynchronously(wg *sync.WaitGroup, queryExpr string, urns []string, dbErrChan chan error) { + // Perform the Assets deletion asynchronously wg.Add(1) go func() { defer wg.Done() @@ -153,38 +159,32 @@ func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun boo close(dbErrChan) }() - // Perform Elasticsearch and Lineage asynchronously if the Assets deletion is successful. + // Perform Elasticsearch and Lineage deletion asynchronously if the Assets deletion is successful wg.Add(1) go func() { defer wg.Done() dbErr := <-dbErrChan if dbErr == nil { - s.deleteAssetsInElasticsearchAsynchronously(queryExpr) - s.deleteAssetsInLineageAsynchronously(urns) + go s.deleteAssetsInElasticsearchByQueryExpr(queryExpr) + go s.deleteAssetsInLineageByQueryExpr(urns) } else { log.Printf("Database deletion failed, skipping Elasticsearch and Lineage deletions: %s", dbErr) } }() - - return uint32(total), nil } -func (s *Service) deleteAssetsInElasticsearchAsynchronously(queryExpr string) { - go func() { - if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(context.Background(), queryExpr); err != nil { - log.Printf("Error occurred during Elasticsearch deletion: %s", err) - } - }() +func (s *Service) deleteAssetsInElasticsearchByQueryExpr(queryExpr string) { + if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(context.Background(), queryExpr); err != nil { + log.Printf("Error occurred during Elasticsearch deletion: %s", err) + } } -func (s *Service) deleteAssetsInLineageAsynchronously(urns []string) { - go func() { - for _, urn := range urns { - if err := s.lineageRepository.DeleteByURN(context.Background(), urn); err != nil { - log.Printf("Error occurred during Lineage deletion: %s", err) - } +func (s *Service) deleteAssetsInLineageByQueryExpr(urns []string) { + for _, urn := range urns { + if err := s.lineageRepository.DeleteByURN(context.Background(), urn); err != nil { + log.Printf("Error occurred during Lineage deletion: %s", err) } - }() + } } func (s *Service) GetAssetByID(ctx context.Context, id string) (Asset, error) { diff --git a/core/asset/service_test.go b/core/asset/service_test.go index 82771bc7..b608691e 100644 --- a/core/asset/service_test.go +++ b/core/asset/service_test.go @@ -445,6 +445,80 @@ func TestService_DeleteAsset(t *testing.T) { } } +func TestService_DeleteAssets(t *testing.T) { + emptyQueryExpr := "" + notMeetIdentifierRequirementQueryExpr := `refreshed_at < now()` + successfulQueryExpr := `refreshed_at <= "2023-12-12 23:59:59" && service in ["service-1", "service-2"] && type == "table"` + type testCase struct { + Description string + QueryExpr string + DryRun bool + Setup func(context.Context, *mocks.AssetRepository, *mocks.DiscoveryRepository, *mocks.LineageRepository) + ExpectAffectedRows uint32 + ExpectErr error + } + + testCases := []testCase{ + { + Description: `should return error if query expr is empty`, + QueryExpr: emptyQueryExpr, + DryRun: false, + Setup: func(ctx context.Context, ar *mocks.AssetRepository, _ *mocks.DiscoveryRepository, _ *mocks.LineageRepository) { + ar.EXPECT().GetCountByQueryExpr(ctx, emptyQueryExpr, true).Return(0, errors.New("error")) + }, + ExpectAffectedRows: 0, + ExpectErr: errors.New("error"), + }, + { + Description: `should return error if query expr does not meet identifier requirement`, + QueryExpr: notMeetIdentifierRequirementQueryExpr, + DryRun: false, + Setup: func(ctx context.Context, ar *mocks.AssetRepository, _ *mocks.DiscoveryRepository, _ *mocks.LineageRepository) { + ar.EXPECT().GetCountByQueryExpr(ctx, notMeetIdentifierRequirementQueryExpr, true). + Return(0, errors.New("must exist these identifiers: refreshed_at, type, and service. Current identifiers: refreshed_at")) + }, + ExpectAffectedRows: 0, + ExpectErr: errors.New("must exist these identifiers: refreshed_at, type, and service. Current identifiers: refreshed_at"), + }, + { + Description: `should only return the numbers of assets that match the given query if dry run is true`, + QueryExpr: successfulQueryExpr, + DryRun: true, + Setup: func(ctx context.Context, ar *mocks.AssetRepository, _ *mocks.DiscoveryRepository, _ *mocks.LineageRepository) { + ar.EXPECT().GetCountByQueryExpr(ctx, successfulQueryExpr, true).Return(11, nil) + }, + ExpectAffectedRows: 11, + ExpectErr: nil, + }, + } + for _, tc := range testCases { + t.Run(tc.Description, func(t *testing.T) { + ctx := context.Background() + + assetRepo := mocks.NewAssetRepository(t) + discoveryRepo := mocks.NewDiscoveryRepository(t) + lineageRepo := mocks.NewLineageRepository(t) + if tc.Setup != nil { + tc.Setup(ctx, assetRepo, discoveryRepo, lineageRepo) + } + + svc := asset.NewService(asset.ServiceDeps{ + AssetRepo: assetRepo, + DiscoveryRepo: discoveryRepo, + LineageRepo: lineageRepo, + Worker: workermanager.NewInSituWorker(workermanager.Deps{DiscoveryRepo: discoveryRepo}), + }) + + affectedRows, err := svc.DeleteAssets(ctx, tc.QueryExpr, tc.DryRun) + + if tc.ExpectErr != nil { + assert.ErrorContains(t, err, tc.ExpectErr.Error()) + } + assert.Equal(t, tc.ExpectAffectedRows, affectedRows) + }) + } +} + func TestService_GetAssetByID(t *testing.T) { assetID := "f742aa61-1100-445c-8d72-355a42e2fb59" urn := "my-test-urn" diff --git a/internal/server/v1beta1/asset_test.go b/internal/server/v1beta1/asset_test.go index 9591d1be..4d5c353d 100644 --- a/internal/server/v1beta1/asset_test.go +++ b/internal/server/v1beta1/asset_test.go @@ -17,6 +17,7 @@ import ( compassv1beta1 "github.com/goto/compass/proto/gotocompany/compass/v1beta1" "github.com/goto/salt/log" "github.com/r3labs/diff/v2" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" @@ -975,9 +976,97 @@ func TestDeleteAsset(t *testing.T) { _, err := handler.DeleteAsset(ctx, &compassv1beta1.DeleteAssetRequest{Id: tc.AssetID}) code := status.Code(err) if code != tc.ExpectStatus { - t.Errorf("expected handler to return Code %s, returned Code %sinstead", tc.ExpectStatus.String(), code.String()) + t.Errorf("expected handler to return Code %s, returned Code %s instead", tc.ExpectStatus.String(), code.String()) + return + } + }) + } +} + +func TestDeleteAssets(t *testing.T) { + var ( + userID = uuid.NewString() + userUUID = uuid.NewString() + emptyQueryExpr = "" + notMeetIdentifierRequirementQueryExpr = `refreshed_at < now()` + successfulQueryExpr = `refreshed_at <= "2023-12-12 23:59:59" && service in ["service-1", "service-2"] && type == "table"` + ) + type TestCase struct { + Description string + QueryExpr string + DryRun bool + ExpectStatus codes.Code + ExpectResult *compassv1beta1.DeleteAssetsResponse + Setup func(ctx context.Context, as *mocks.AssetService, astID string) + } + + testCases := []TestCase{ + { + Description: "should return error when insert empty query expr", + QueryExpr: emptyQueryExpr, + DryRun: false, + ExpectStatus: codes.InvalidArgument, + ExpectResult: nil, + Setup: func(ctx context.Context, as *mocks.AssetService, astID string) { + as.EXPECT().DeleteAssets(ctx, emptyQueryExpr, false).Return(0, errors.New("error")) + }, + }, + { + Description: "should return error when query expr does not meet identifier requirement", + QueryExpr: notMeetIdentifierRequirementQueryExpr, + DryRun: false, + ExpectStatus: codes.InvalidArgument, + ExpectResult: nil, + Setup: func(ctx context.Context, as *mocks.AssetService, astID string) { + as.EXPECT().DeleteAssets(ctx, notMeetIdentifierRequirementQueryExpr, false). + Return(0, errors.New("must exist these identifiers: refreshed_at, type, and service. Current identifiers: refreshed_at")) + }, + }, + { + Description: `should only return the numbers of assets that match the given query if dry run is true`, + QueryExpr: successfulQueryExpr, + DryRun: true, + ExpectStatus: codes.OK, + ExpectResult: &compassv1beta1.DeleteAssetsResponse{AffectedRows: 11}, + Setup: func(ctx context.Context, as *mocks.AssetService, astID string) { + as.EXPECT().DeleteAssets(ctx, successfulQueryExpr, true).Return(11, nil) + }, + }, + { + Description: `should return the affected rows numbers and perform deletion in the background if dry run is false`, + QueryExpr: successfulQueryExpr, + DryRun: false, + ExpectStatus: codes.OK, + ExpectResult: &compassv1beta1.DeleteAssetsResponse{AffectedRows: 2}, + Setup: func(ctx context.Context, as *mocks.AssetService, astID string) { + as.EXPECT().DeleteAssets(ctx, successfulQueryExpr, false).Return(2, nil) + }, + }, + } + for _, tc := range testCases { + t.Run(tc.Description, func(t *testing.T) { + ctx := user.NewContext(context.Background(), user.User{UUID: userUUID}) + + logger := log.NewNoop() + mockUserSvc := new(mocks.UserService) + mockAssetSvc := new(mocks.AssetService) + if tc.Setup != nil { + tc.Setup(ctx, mockAssetSvc, assetID) + } + defer mockUserSvc.AssertExpectations(t) + defer mockAssetSvc.AssertExpectations(t) + + mockUserSvc.EXPECT().ValidateUser(ctx, userUUID, "").Return(userID, nil) + + handler := NewAPIServer(APIServerDeps{AssetSvc: mockAssetSvc, UserSvc: mockUserSvc, Logger: logger}) + + result, err := handler.DeleteAssets(ctx, &compassv1beta1.DeleteAssetsRequest{QueryExpr: tc.QueryExpr, DryRun: tc.DryRun}) + code := status.Code(err) + if code != tc.ExpectStatus { + t.Errorf("expected handler to return Code %s, returned Code %s instead", tc.ExpectStatus.String(), code.String()) return } + assert.Equal(t, tc.ExpectResult, result) }) } } diff --git a/internal/store/elasticsearch/discovery_repository_test.go b/internal/store/elasticsearch/discovery_repository_test.go index 98ffda58..ab4ca9cb 100644 --- a/internal/store/elasticsearch/discovery_repository_test.go +++ b/internal/store/elasticsearch/discovery_repository_test.go @@ -9,6 +9,7 @@ import ( "github.com/goto/compass/core/asset" store "github.com/goto/compass/internal/store/elasticsearch" + queryexpr "github.com/goto/compass/pkg/query_expr" "github.com/goto/salt/log" "github.com/olivere/elastic/v7" "github.com/stretchr/testify/assert" @@ -391,6 +392,111 @@ func TestDiscoveryRepositoryDeleteByURN(t *testing.T) { }) } +func TestDiscoveryRepositoryDeleteByQueryExpr(t *testing.T) { + var ( + ctx = context.Background() + bigqueryService = "bigquery-test" + kafkaService = "kafka-test" + ) + + cli, err := esTestServer.NewClient() + require.NoError(t, err) + + esClient, err := store.NewClient( + log.NewNoop(), store.Config{}, store.WithClient(cli), + ) + require.NoError(t, err) + + repo := store.NewDiscoveryRepository(esClient, log.NewNoop(), time.Second*10, []string{"number", "id"}) + + t.Run("should return error if the given query expr is empty", func(t *testing.T) { + err = repo.DeleteByQueryExpr(ctx, "") + assert.ErrorIs(t, err, asset.ErrEmptyQuery) + }) + + t.Run("should not return error on success", func(t *testing.T) { + ast := asset.Asset{ + ID: "delete-id", + Type: asset.TypeTable, + Service: bigqueryService, + URN: "some-urn", + RefreshedAt: time.Now(), + } + + err = repo.Upsert(ctx, ast) + require.NoError(t, err) + + queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + "' && service == '" + bigqueryService + + "' && type == '" + asset.TypeTable.String() + "'" + err = repo.DeleteByQueryExpr(ctx, queryExpr) + assert.NoError(t, err) + + deleteAssetESExpr := &store.DeleteAssetESExpr{ + ESExpr: queryexpr.ESExpr(queryExpr), + } + esQuery, _ := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) + + res, err := cli.Search( + cli.Search.WithBody(strings.NewReader(esQuery)), + cli.Search.WithIndex("_all"), + ) + require.NoError(t, err) + assert.False(t, res.IsError()) + + var body struct { + Hits struct { + Total elastic.TotalHits `json:"total"` + } `json:"hits"` + } + require.NoError(t, json.NewDecoder(res.Body).Decode(&body)) + assert.Equal(t, int64(0), body.Hits.Total.Value) + }) + + t.Run("should ignore unavailable indices", func(t *testing.T) { + currentTime := time.Now() + ast1 := asset.Asset{ + ID: "id1", + Type: asset.TypeTable, + Service: bigqueryService, + URN: "urn1", + RefreshedAt: currentTime, + } + ast2 := asset.Asset{ + ID: "id2", + Type: asset.TypeTopic, + Service: kafkaService, + URN: "urn2", + RefreshedAt: currentTime, + } + cli, err := esTestServer.NewClient() + require.NoError(t, err) + esClient, err := store.NewClient( + log.NewNoop(), + store.Config{}, + store.WithClient(cli), + ) + require.NoError(t, err) + + repo := store.NewDiscoveryRepository(esClient, log.NewNoop(), time.Second*10, []string{"number", "id"}) + + err = repo.Upsert(ctx, ast1) + require.NoError(t, err) + + err = repo.Upsert(ctx, ast2) + require.NoError(t, err) + + _, err = cli.Indices.Close([]string{kafkaService}) + require.NoError(t, err) + + queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + "' && service == '" + kafkaService + + "' && type == '" + asset.TypeTopic.String() + "'" + err = repo.DeleteByQueryExpr(ctx, queryExpr) + assert.NoError(t, err) + }) +} + func TestDiscoveryRepository_SyncAssets(t *testing.T) { t.Run("should return success", func(t *testing.T) { var ( diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index 1e4c69ac..ff6324f6 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -43,7 +43,7 @@ func (d *DeleteAssetSQLExpr) Validate() error { generichelper.Contains(identifiers, "type") && generichelper.Contains(identifiers, "service") if !mustExist { - return fmt.Errorf("must exists these identifiers: refreshed_at, type, and service. "+ + return fmt.Errorf("must exist these identifiers: refreshed_at, type, and service. "+ "Current identifiers: %v", identifiers) } diff --git a/internal/store/postgres/asset_repository_test.go b/internal/store/postgres/asset_repository_test.go index 13059c47..07da42a5 100644 --- a/internal/store/postgres/asset_repository_test.go +++ b/internal/store/postgres/asset_repository_test.go @@ -1316,6 +1316,52 @@ func (r *AssetRepositoryTestSuite) TestDeleteByURN() { }) } +func (r *AssetRepositoryTestSuite) TestDeleteByQueryExpr() { + r.Run("should delete correct asset", func() { + currentTime := time.Now() + asset1 := asset.Asset{ + URN: "urn-del-1", + Type: "table", + Service: "bigquery", + UpdatedBy: user.User{ID: defaultAssetUpdaterUserID}, + RefreshedAt: currentTime.AddDate(-1, 0, 0), + } + asset2 := asset.Asset{ + URN: "urn-del-2", + Type: "topic", + Service: "kafka", + Version: asset.BaseVersion, + UpdatedBy: user.User{ID: defaultAssetUpdaterUserID}, + RefreshedAt: currentTime.AddDate(-1, 0, 0), + } + + _, err := r.repository.Upsert(r.ctx, &asset1) + r.Require().NoError(err) + + id, err := r.repository.Upsert(r.ctx, &asset2) + r.Require().NoError(err) + + queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + "' && service == '" + asset1.Service + + "' && type == '" + asset1.Type.String() + + "' && urn == '" + asset1.URN + "'" + urns, err := r.repository.DeleteByQueryExpr(r.ctx, queryExpr) + r.NoError(err) + r.Equal([]string{"urn-del-1"}, urns) + + _, err = r.repository.GetByURN(r.ctx, asset1.URN) + r.ErrorAs(err, &asset.NotFoundError{URN: asset1.URN}) + + asset2FromDB, err := r.repository.GetByURN(r.ctx, asset2.URN) + r.NoError(err) + r.Equal(id, asset2FromDB.ID) + + // cleanup + err = r.repository.DeleteByURN(r.ctx, asset2.URN) + r.NoError(err) + }) +} + func (r *AssetRepositoryTestSuite) TestAddProbe() { r.Run("return NotFoundError if asset does not exist", func() { urn := "invalid-urn" diff --git a/internal/workermanager/discovery_worker.go b/internal/workermanager/discovery_worker.go index add8fe67..7fac7fe5 100644 --- a/internal/workermanager/discovery_worker.go +++ b/internal/workermanager/discovery_worker.go @@ -165,7 +165,7 @@ func (m *Manager) EnqueueDeleteAssetsByQueryExprJob(ctx context.Context, queryEx Payload: ([]byte)(queryExpr), }) if err != nil { - return fmt.Errorf("enqueue delete asset job: %w: query expr '%s'", err, queryExpr) + return fmt.Errorf("enqueue delete asset job: %w: query expr: '%s'", err, queryExpr) } return nil @@ -186,7 +186,7 @@ func (m *Manager) DeleteAssetsByQueryExpr(ctx context.Context, job worker.JobSpe queryExpr := (string)(job.Payload) if err := m.discoveryRepo.DeleteByQueryExpr(ctx, queryExpr); err != nil { return &worker.RetryableError{ - Cause: fmt.Errorf("delete asset from discovery repo: %w: query expr '%s'", err, queryExpr), + Cause: fmt.Errorf("delete asset from discovery repo: %w: query expr: '%s'", err, queryExpr), } } return nil diff --git a/internal/workermanager/discovery_worker_test.go b/internal/workermanager/discovery_worker_test.go index a3ee8886..c30793fc 100644 --- a/internal/workermanager/discovery_worker_test.go +++ b/internal/workermanager/discovery_worker_test.go @@ -3,6 +3,7 @@ package workermanager_test import ( "errors" "testing" + "time" "github.com/goto/compass/core/asset" "github.com/goto/compass/internal/testutils" @@ -32,7 +33,7 @@ func TestManager_EnqueueIndexAssetJob(t *testing.T) { wrkr := mocks.NewWorker(t) wrkr.EXPECT(). Enqueue(ctx, worker.JobSpec{ - Type: "index-asset", + Type: workermanager.JobIndexAsset, Payload: testutils.Marshal(t, sampleAsset), }). Return(tc.enqueueErr) @@ -74,7 +75,7 @@ func TestManager_IndexAsset(t *testing.T) { DiscoveryRepo: discoveryRepo, }) err := mgr.IndexAsset(ctx, worker.JobSpec{ - Type: "index-asset", + Type: workermanager.JobIndexAsset, Payload: testutils.Marshal(t, sampleAsset), }) if tc.expectedErr { @@ -106,7 +107,7 @@ func TestManager_EnqueueDeleteAssetJob(t *testing.T) { wrkr := mocks.NewWorker(t) wrkr.EXPECT(). Enqueue(ctx, worker.JobSpec{ - Type: "delete-asset", + Type: workermanager.JobDeleteAsset, Payload: []byte("some-urn"), }). Return(tc.enqueueErr) @@ -146,7 +147,7 @@ func TestManager_DeleteAsset(t *testing.T) { DiscoveryRepo: discoveryRepo, }) err := mgr.DeleteAsset(ctx, worker.JobSpec{ - Type: "index-asset", + Type: workermanager.JobDeleteAsset, Payload: []byte("some-urn"), }) if tc.expectedErr { @@ -159,3 +160,83 @@ func TestManager_DeleteAsset(t *testing.T) { }) } } + +func TestManager_EnqueueDeleteAssetsByQueryExprJob(t *testing.T) { + cases := []struct { + name string + enqueueErr error + expectedErr string + }{ + {name: "Success"}, + { + name: "Failure", + enqueueErr: errors.New("fail"), + expectedErr: "enqueue delete asset job: fail: query expr:", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + "' && service == 'test-service'" + + "' && type == 'table'" + + "' && urn == 'some-urn'" + wrkr := mocks.NewWorker(t) + wrkr.EXPECT(). + Enqueue(ctx, worker.JobSpec{ + Type: workermanager.JobDeleteAssetsByQuery, + Payload: []byte(queryExpr), + }). + Return(tc.enqueueErr) + + mgr := workermanager.NewWithWorker(wrkr, workermanager.Deps{}) + err := mgr.EnqueueDeleteAssetsByQueryExprJob(ctx, queryExpr) + if tc.expectedErr != "" { + assert.ErrorContains(t, err, tc.expectedErr) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestManager_DeleteAssets(t *testing.T) { + cases := []struct { + name string + discoveryErr error + expectedErr bool + }{ + {name: "Success"}, + { + name: "failure", + discoveryErr: errors.New("fail"), + expectedErr: true, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + "' && service == 'test-service'" + + "' && type == 'table'" + + "' && urn == 'some-urn'" + discoveryRepo := mocks.NewDiscoveryRepository(t) + discoveryRepo.EXPECT(). + DeleteByQueryExpr(ctx, queryExpr). + Return(tc.discoveryErr) + + mgr := workermanager.NewWithWorker(mocks.NewWorker(t), workermanager.Deps{ + DiscoveryRepo: discoveryRepo, + }) + err := mgr.DeleteAssetsByQueryExpr(ctx, worker.JobSpec{ + Type: workermanager.JobDeleteAssetsByQuery, + Payload: []byte(queryExpr), + }) + if tc.expectedErr { + var re *worker.RetryableError + assert.ErrorAs(t, err, &re) + assert.ErrorIs(t, err, tc.discoveryErr) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/internal/workermanager/in_situ_worker.go b/internal/workermanager/in_situ_worker.go index dfdca828..8f395e0e 100644 --- a/internal/workermanager/in_situ_worker.go +++ b/internal/workermanager/in_situ_worker.go @@ -42,7 +42,7 @@ func (m *InSituWorker) EnqueueDeleteAssetJob(ctx context.Context, urn string) er func (m *InSituWorker) EnqueueDeleteAssetsByQueryExprJob(ctx context.Context, queryExpr string) error { if err := m.discoveryRepo.DeleteByQueryExpr(ctx, queryExpr); err != nil { - return fmt.Errorf("delete asset from discovery repo: %w: query expr '%s'", err, queryExpr) + return fmt.Errorf("delete asset from discovery repo: %w: query expr: '%s'", err, queryExpr) } return nil } diff --git a/internal/workermanager/in_situ_worker_test.go b/internal/workermanager/in_situ_worker_test.go index 84b6fd0f..d8a745d8 100644 --- a/internal/workermanager/in_situ_worker_test.go +++ b/internal/workermanager/in_situ_worker_test.go @@ -3,6 +3,7 @@ package workermanager_test import ( "errors" "testing" + "time" "github.com/goto/compass/core/asset" "github.com/goto/compass/internal/workermanager" @@ -79,3 +80,41 @@ func TestInSituWorker_EnqueueDeleteAssetJob(t *testing.T) { }) } } + +func TestInSituWorker_EnqueueDeleteAssetsByQueryExprJob(t *testing.T) { + cases := []struct { + name string + discoveryErr error + expectedErr bool + }{ + {name: "Success"}, + { + name: "Failure", + discoveryErr: errors.New("fail"), + expectedErr: true, + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + "' && service == 'test-service'" + + "' && type == 'table'" + + "' && urn == 'some-urn'" + discoveryRepo := mocks.NewDiscoveryRepository(t) + discoveryRepo.EXPECT(). + DeleteByQueryExpr(ctx, queryExpr). + Return(tc.discoveryErr) + + wrkr := workermanager.NewInSituWorker(workermanager.Deps{ + DiscoveryRepo: discoveryRepo, + }) + err := wrkr.EnqueueDeleteAssetsByQueryExprJob(ctx, queryExpr) + if tc.expectedErr { + assert.Error(t, err) + assert.ErrorIs(t, err, tc.discoveryErr) + } else { + assert.NoError(t, err) + } + }) + } +} From 57897eadb95befb7025f2111c62e65380f696610 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Mon, 12 Aug 2024 18:51:49 +0700 Subject: [PATCH 27/45] test: create unit test for converter --- pkg/query_expr/es_expr_test.go | 98 ++++++++++++++++++++++ pkg/query_expr/query_expr_test.go | 134 ++++++++++++++++++++++++++++++ pkg/query_expr/sql_expr.go | 23 ++--- pkg/query_expr/sql_expr_test.go | 98 ++++++++++++++++++++++ 4 files changed, 343 insertions(+), 10 deletions(-) create mode 100644 pkg/query_expr/es_expr_test.go create mode 100644 pkg/query_expr/query_expr_test.go create mode 100644 pkg/query_expr/sql_expr_test.go diff --git a/pkg/query_expr/es_expr_test.go b/pkg/query_expr/es_expr_test.go new file mode 100644 index 00000000..93a2a75f --- /dev/null +++ b/pkg/query_expr/es_expr_test.go @@ -0,0 +1,98 @@ +package queryexpr_test + +import ( + "testing" + + queryexpr "github.com/goto/compass/pkg/query_expr" +) + +func TestESExpr_String(t *testing.T) { + tests := []struct { + expr queryexpr.SQLExpr + want string + }{ + { + expr: queryexpr.SQLExpr("test"), + want: "test", + }, + { + expr: queryexpr.SQLExpr("bool_identifier == !(findLast([1, 2, 3, 4], # > 2) == 4)"), + want: "bool_identifier == !(findLast([1, 2, 3, 4], # > 2) == 4)", + }, + } + for i, tt := range tests { + t.Run("test-case-"+string(rune(i)), func(t *testing.T) { + if got := tt.expr.String(); got != tt.want { + t.Errorf("String() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestESExpr_ToQuery(t *testing.T) { + tests := []struct { + name string + expr queryexpr.ESExpr + want string + wantErr bool + }{ + { + name: "less than condition with single quote", + expr: queryexpr.ESExpr(`updated_at < '2024-04-05 23:59:59'`), + want: `{"query":{"range":{"updated_at":{"lt":"2024-04-05 23:59:59"}}}}`, + wantErr: false, + }, + { + name: "greater than condition with double quote", + expr: queryexpr.ESExpr(`updated_at > "2024-04-05 23:59:59"`), + want: `{"query":{"range":{"updated_at":{"gt":"2024-04-05 23:59:59"}}}}`, + wantErr: false, + }, + { + name: "in condition", + expr: queryexpr.ESExpr(`service in ["test1","test2","test3"]`), + want: `{"query":{"terms":{"service":["test1","test2","test3"]}}}`, + wantErr: false, + }, + { + name: "equals or not in condition", + expr: queryexpr.ESExpr(`name == "John" || service not in ["test1","test2","test3"]`), + want: `{"query":{"bool":{"should":[{"term":{"name":"John"}},{"bool":{"must_not":[{"terms":{"service":["test1","test2","test3"]}}]}}]}}}`, + wantErr: false, + }, + { + name: "complex query expression that can directly produce a value", + expr: queryexpr.ESExpr(`bool_identifier == !(findLast([1, 2, 3, 4], # > 2) == 4)`), + want: `{"query":{"term":{"bool_identifier":false}}}`, + wantErr: false, + }, + { + name: "complex query expression that can NOT directly produce a value", + expr: queryexpr.ESExpr(`service in filter(assets, .Service startsWith "T")`), + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.expr.ToQuery() + if (err != nil) != tt.wantErr { + t.Errorf("ToQuery() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ToQuery() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestESExpr_Validate(t *testing.T) { + t.Run("should return nil as default validation", func(t *testing.T) { + expr := queryexpr.ESExpr("query_es == 'test'") + if err := (&expr).Validate(); err != nil { + t.Errorf("Validate() error = %v, wantErr %v", err, nil) + } + return + }) +} diff --git a/pkg/query_expr/query_expr_test.go b/pkg/query_expr/query_expr_test.go new file mode 100644 index 00000000..748628fa --- /dev/null +++ b/pkg/query_expr/query_expr_test.go @@ -0,0 +1,134 @@ +package queryexpr + +import ( + "reflect" + "testing" +) + +func TestGetIdentifiers(t *testing.T) { + tests := []struct { + name string + expr string + want []string + wantErr bool + }{ + { + name: "got 0 identifier test", + expr: `findLast([1, 2, 3, 4], # > 2)`, + want: nil, + wantErr: false, + }, + { + name: "got 1 identifiers test", + expr: `updated_at < '2024-04-05 23:59:59'`, + want: []string{"updated_at"}, + wantErr: false, + }, + { + name: "got 3 identifiers test", + expr: `(identifier1 == !(findLast([1, 2, 3, 4], # > 2) == 4)) && identifier2 != 'John' || identifier3 == "hallo"`, + want: []string{"identifier1", "identifier2", "identifier3"}, + wantErr: false, + }, + { + name: "got error", + expr: `findLast([1, 2, 3, 4], # > 2`, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetIdentifiers(tt.expr) + if (err != nil) != tt.wantErr { + t.Errorf("GetIdentifiers() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetIdentifiers() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetQueryExprResult(t *testing.T) { + tests := []struct { + name string + expr string + want any + wantErr bool + }{ + { + name: "return a value from func", + expr: `findLast([1, 2, 3, 4], # > 2)`, + want: 4, + wantErr: false, + }, + { + name: "return a value func equation", + expr: `false == !true`, + want: true, + wantErr: false, + }, + { + name: "got error due to can NOT directly produce a value", + expr: `updated_at < '2024-04-05 23:59:59'`, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetQueryExprResult(tt.expr) + if (err != nil) != tt.wantErr { + t.Errorf("GetQueryExprResult() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetQueryExprResult() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetTreeNodeFromQueryExpr(t *testing.T) { + tests := []struct { + name string + expr string + want any + wantErr bool + }{ + { + name: "success using func from the library", + expr: `findLast([1], # >= 1)`, + want: "findLast([1], # >= 1)", + wantErr: false, + }, + { + name: "success using equation", + expr: `false == !true`, + want: "false == !true", + wantErr: false, + }, + { + name: "got error using wrong syntax", + expr: `findLast(`, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetTreeNodeFromQueryExpr(tt.expr) + if (err != nil) != tt.wantErr { + t.Errorf("GetTreeNodeFromQueryExpr() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + if got.String() != tt.want { + t.Errorf("GetTreeNodeFromQueryExpr() got = %v, want %v", got, tt.want) + } + } + }) + } +} diff --git a/pkg/query_expr/sql_expr.go b/pkg/query_expr/sql_expr.go index 9d081038..ec5e3c52 100644 --- a/pkg/query_expr/sql_expr.go +++ b/pkg/query_expr/sql_expr.go @@ -21,7 +21,7 @@ func (s *SQLExpr) ToQuery() (string, error) { return "", err } stringBuilder := &strings.Builder{} - if err := s.ConvertToSQL(queryExprParsed, stringBuilder); err != nil { + if err := s.convertToSQL(queryExprParsed, stringBuilder); err != nil { return "", err } return stringBuilder.String(), nil @@ -32,9 +32,9 @@ func (*SQLExpr) Validate() error { return nil } -// ConvertToSQL The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. +// convertToSQL The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. // TODO: implement translator for node type that still not covered right now. -func (s *SQLExpr) ConvertToSQL(node ast.Node, stringBuilder *strings.Builder) error { +func (s *SQLExpr) convertToSQL(node ast.Node, stringBuilder *strings.Builder) error { if node == nil { return fmt.Errorf("cannot convert nil to SQL query") } @@ -62,7 +62,7 @@ func (s *SQLExpr) ConvertToSQL(node ast.Node, stringBuilder *strings.Builder) er if err := s.patchUnaryNode(n); err != nil { return err } - if err := s.ConvertToSQL(n.Node, stringBuilder); err != nil { + if err := s.convertToSQL(n.Node, stringBuilder); err != nil { return err } case *ast.ArrayNode: @@ -93,14 +93,14 @@ func (s *SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings fmt.Fprintf(stringBuilder, "%v", result) } else { stringBuilder.WriteString("(") - if err := s.ConvertToSQL(n.Left, stringBuilder); err != nil { + if err := s.convertToSQL(n.Left, stringBuilder); err != nil { return err } // write operator fmt.Fprintf(stringBuilder, " %s ", strings.ToUpper(operator)) - if err := s.ConvertToSQL(n.Right, stringBuilder); err != nil { + if err := s.convertToSQL(n.Right, stringBuilder); err != nil { return err } stringBuilder.WriteString(")") @@ -112,7 +112,7 @@ func (s *SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings func (s *SQLExpr) arrayNodeToSQLQuery(n *ast.ArrayNode, stringBuilder *strings.Builder) error { stringBuilder.WriteString("(") for i := range n.Nodes { - if err := s.ConvertToSQL(n.Nodes[i], stringBuilder); err != nil { + if err := s.convertToSQL(n.Nodes[i], stringBuilder); err != nil { return err } if i != len(n.Nodes)-1 { @@ -127,7 +127,7 @@ func (s *SQLExpr) patchUnaryNode(n *ast.UnaryNode) error { switch n.Operator { case "not": binaryNode, ok := (n.Node).(*ast.BinaryNode) - if ok && binaryNode.Operator == "in" { + if ok && strings.ToUpper(binaryNode.Operator) == "IN" { ast.Patch(&n.Node, &ast.BinaryNode{ Operator: "not in", Left: binaryNode.Left, @@ -149,7 +149,7 @@ func (s *SQLExpr) patchUnaryNode(n *ast.UnaryNode) error { } if boolResult, ok := result.(bool); ok { ast.Patch(&n.Node, &ast.BoolNode{ - Value: !boolResult, + Value: boolResult, }) return nil } @@ -161,7 +161,7 @@ func (s *SQLExpr) patchUnaryNode(n *ast.UnaryNode) error { } func (*SQLExpr) operatorToSQL(bn *ast.BinaryNode) string { - switch bn.Operator { + switch strings.ToUpper(bn.Operator) { case "&&": return "AND" case "||": @@ -170,6 +170,7 @@ func (*SQLExpr) operatorToSQL(bn *ast.BinaryNode) string { if _, ok := bn.Right.(*ast.NilNode); ok { return "IS NOT" } + return bn.Operator case "==": if _, ok := bn.Right.(*ast.NilNode); ok { return "IS" @@ -177,6 +178,8 @@ func (*SQLExpr) operatorToSQL(bn *ast.BinaryNode) string { return "=" case "<", "<=", ">", ">=": return bn.Operator + case "IN", "NOT IN": + return bn.Operator } return "" // identify operation, like: +, -, *, etc diff --git a/pkg/query_expr/sql_expr_test.go b/pkg/query_expr/sql_expr_test.go new file mode 100644 index 00000000..fa4eb52e --- /dev/null +++ b/pkg/query_expr/sql_expr_test.go @@ -0,0 +1,98 @@ +package queryexpr_test + +import ( + "testing" + + queryexpr "github.com/goto/compass/pkg/query_expr" +) + +func TestSQLExpr_String(t *testing.T) { + tests := []struct { + expr queryexpr.SQLExpr + want string + }{ + { + expr: queryexpr.SQLExpr("test"), + want: "test", + }, + { + expr: queryexpr.SQLExpr("bool_identifier == !(findLast([1, 2, 3, 4], # > 2) == 4)"), + want: "bool_identifier == !(findLast([1, 2, 3, 4], # > 2) == 4)", + }, + } + for i, tt := range tests { + t.Run("test-case-"+string(rune(i)), func(t *testing.T) { + if got := tt.expr.String(); got != tt.want { + t.Errorf("String() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSQLExpr_ToQuery(t *testing.T) { + tests := []struct { + name string + expr queryexpr.SQLExpr + want string + wantErr bool + }{ + { + name: "less than condition with single quote", + expr: queryexpr.SQLExpr(`updated_at < '2024-04-05 23:59:59'`), + want: `(updated_at < '2024-04-05 23:59:59')`, + wantErr: false, + }, + { + name: "greater than condition with double quote", + expr: queryexpr.SQLExpr(`updated_at > "2024-04-05 23:59:59"`), + want: `(updated_at > '2024-04-05 23:59:59')`, + wantErr: false, + }, + { + name: "in condition", + expr: queryexpr.SQLExpr(`service in ["test1","test2","test3"]`), + want: `(service IN ('test1', 'test2', 'test3'))`, + wantErr: false, + }, + { + name: "equals or not in condition", + expr: queryexpr.SQLExpr(`name == "John" || service not in ["test1","test2","test3"]`), + want: `((name = 'John') OR (service NOT IN ('test1', 'test2', 'test3')))`, + wantErr: false, + }, + { + name: "complex query expression that can directly produce a value", + expr: queryexpr.SQLExpr(`(bool_identifier == !(findLast([1, 2, 3, 4], # > 2) == 4)) && name != 'John'`), + want: `((bool_identifier = false) AND (name != 'John'))`, + wantErr: false, + }, + { + name: "complex query expression that can NOT directly produce a value", + expr: queryexpr.SQLExpr(`service in filter(assets, .Service startsWith "T")`), + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.expr.ToQuery() + if (err != nil) != tt.wantErr { + t.Errorf("ToQuery() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ToQuery() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSQLExpr_Validate(t *testing.T) { + t.Run("should return nil as default validation", func(t *testing.T) { + expr := queryexpr.SQLExpr("query_sql == 'test'") + if err := (&expr).Validate(); err != nil { + t.Errorf("Validate() error = %v, wantErr %v", err, nil) + } + return + }) +} From e4903d0a9c3c4a3720478ce3684b2d5dedae6d26 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Tue, 13 Aug 2024 00:39:13 +0700 Subject: [PATCH 28/45] feat: add validation in deletion query and remove redundant code --- internal/server/v1beta1/asset.go | 7 +- .../elasticsearch/discovery_repository.go | 26 +----- .../discovery_repository_test.go | 5 +- internal/store/postgres/asset_repository.go | 32 ++----- pkg/generic_helper/generic_helper.go | 65 +++++++++++++ pkg/generic_helper/generic_helper_test.go | 93 +++++++++++++++++++ pkg/query_expr/delete_asset_expr.go | 49 ++++++++++ pkg/query_expr/es_expr_test.go | 1 - .../equals-or-not-in-condition.json | 28 ------ pkg/query_expr/es_test_data/in-condition.json | 11 --- pkg/query_expr/es_test_data/lt-condition.json | 9 -- pkg/query_expr/query_expr.go | 18 ++-- pkg/query_expr/query_expr_test.go | 38 +++++--- pkg/query_expr/sql_expr.go | 7 +- pkg/query_expr/sql_expr_test.go | 1 - 15 files changed, 264 insertions(+), 126 deletions(-) create mode 100644 pkg/generic_helper/generic_helper_test.go create mode 100644 pkg/query_expr/delete_asset_expr.go delete mode 100644 pkg/query_expr/es_test_data/equals-or-not-in-condition.json delete mode 100644 pkg/query_expr/es_test_data/in-condition.json delete mode 100644 pkg/query_expr/es_test_data/lt-condition.json diff --git a/internal/server/v1beta1/asset.go b/internal/server/v1beta1/asset.go index 93c2acac..2abf5a31 100644 --- a/internal/server/v1beta1/asset.go +++ b/internal/server/v1beta1/asset.go @@ -308,12 +308,17 @@ func (server *APIServer) DeleteAsset(ctx context.Context, req *compassv1beta1.De } func (server *APIServer) DeleteAssets(ctx context.Context, req *compassv1beta1.DeleteAssetsRequest) (*compassv1beta1.DeleteAssetsResponse, error) { + var affectedRows uint32 _, err := server.ValidateUserInCtx(ctx) if err != nil { return nil, err } + defer func() { + server.logger.Warn("the number of affected rows is %d", affectedRows) + }() - affectedRows, err := server.assetService.DeleteAssets(ctx, req.QueryExpr, req.DryRun) + server.logger.Warn("delete request: %v", req) + affectedRows, err = server.assetService.DeleteAssets(ctx, req.QueryExpr, req.DryRun) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } diff --git a/internal/store/elasticsearch/discovery_repository.go b/internal/store/elasticsearch/discovery_repository.go index 9acb1ce2..0cc0362f 100644 --- a/internal/store/elasticsearch/discovery_repository.go +++ b/internal/store/elasticsearch/discovery_repository.go @@ -12,7 +12,6 @@ import ( "time" "github.com/goto/compass/core/asset" - generichelper "github.com/goto/compass/pkg/generic_helper" queryexpr "github.com/goto/compass/pkg/query_expr" "github.com/goto/salt/log" ) @@ -26,26 +25,6 @@ type DiscoveryRepository struct { columnSearchExclusionList []string } -type DeleteAssetESExpr struct { - queryexpr.ESExpr -} - -func (d *DeleteAssetESExpr) Validate() error { - identifiers, err := queryexpr.GetIdentifiers(d.String()) - if err != nil { - return err - } - - mustExist := generichelper.Contains(identifiers, "refreshed_at") && - generichelper.Contains(identifiers, "type") && - generichelper.Contains(identifiers, "service") - if !mustExist { - return fmt.Errorf("must exists these identifiers: refreshed_at, type. Current identifiers: %v", identifiers) - } - - return nil -} - func NewDiscoveryRepository(cli *Client, logger log.Logger, requestTimeout time.Duration, colSearchExclusionList []string) *DiscoveryRepository { return &DiscoveryRepository{ cli: cli, @@ -171,8 +150,9 @@ func (repo *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExp return asset.ErrEmptyQuery } - deleteAssetESExpr := &DeleteAssetESExpr{ - queryexpr.ESExpr(queryExpr), + expr := queryexpr.ESExpr(queryExpr) + deleteAssetESExpr := &queryexpr.DeleteAssetExpr{ + ExprStr: &expr, } esQuery, err := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) if err != nil { diff --git a/internal/store/elasticsearch/discovery_repository_test.go b/internal/store/elasticsearch/discovery_repository_test.go index ab4ca9cb..dacd0101 100644 --- a/internal/store/elasticsearch/discovery_repository_test.go +++ b/internal/store/elasticsearch/discovery_repository_test.go @@ -432,8 +432,9 @@ func TestDiscoveryRepositoryDeleteByQueryExpr(t *testing.T) { err = repo.DeleteByQueryExpr(ctx, queryExpr) assert.NoError(t, err) - deleteAssetESExpr := &store.DeleteAssetESExpr{ - ESExpr: queryexpr.ESExpr(queryExpr), + expr := queryexpr.ESExpr(queryExpr) + deleteAssetESExpr := &queryexpr.DeleteAssetExpr{ + ExprStr: &expr, } esQuery, _ := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index ff6324f6..816f85c5 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -13,7 +13,6 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/goto/compass/core/asset" "github.com/goto/compass/core/user" - generichelper "github.com/goto/compass/pkg/generic_helper" queryexpr "github.com/goto/compass/pkg/query_expr" "github.com/jmoiron/sqlx" "github.com/r3labs/diff/v2" @@ -29,27 +28,6 @@ type AssetRepository struct { defaultUserProvider string } -type DeleteAssetSQLExpr struct { - queryexpr.SQLExpr -} - -func (d *DeleteAssetSQLExpr) Validate() error { - identifiers, err := queryexpr.GetIdentifiers(d.String()) - if err != nil { - return err - } - - mustExist := generichelper.Contains(identifiers, "refreshed_at") && - generichelper.Contains(identifiers, "type") && - generichelper.Contains(identifiers, "service") - if !mustExist { - return fmt.Errorf("must exist these identifiers: refreshed_at, type, and service. "+ - "Current identifiers: %v", identifiers) - } - - return nil -} - // GetAll retrieves list of assets with filters func (r *AssetRepository) GetAll(ctx context.Context, flt asset.Filter) ([]asset.Asset, error) { builder := r.getAssetSQL().Offset(uint64(flt.Offset)) @@ -142,8 +120,9 @@ func (r *AssetRepository) GetCount(ctx context.Context, flt asset.Filter) (int, func (r *AssetRepository) GetCountByQueryExpr(ctx context.Context, queryExpr string, isDeleteExpr bool) (int, error) { var sqlQuery string if isDeleteExpr { - deleteExpr := &DeleteAssetSQLExpr{ - queryexpr.SQLExpr(queryExpr), + expr := queryexpr.SQLExpr(queryExpr) + deleteExpr := &queryexpr.DeleteAssetExpr{ + ExprStr: &expr, } query, err := queryexpr.ValidateAndGetQueryFromExpr(deleteExpr) if err != nil { @@ -424,8 +403,9 @@ func (r *AssetRepository) DeleteByURN(ctx context.Context, urn string) error { func (r *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) ([]string, error) { var allURNs []string err := r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { - deleteExpr := &DeleteAssetSQLExpr{ - queryexpr.SQLExpr(queryExpr), + expr := queryexpr.SQLExpr(queryExpr) + deleteExpr := &queryexpr.DeleteAssetExpr{ + ExprStr: &expr, } query, err := queryexpr.ValidateAndGetQueryFromExpr(deleteExpr) if err != nil { diff --git a/pkg/generic_helper/generic_helper.go b/pkg/generic_helper/generic_helper.go index 178f1114..768a0006 100644 --- a/pkg/generic_helper/generic_helper.go +++ b/pkg/generic_helper/generic_helper.go @@ -1,6 +1,19 @@ package generichelper +import ( + "reflect" +) + // Contains checks if a target item exists in an array of any type. +// +// Example +// +// names := []string{"Alice", "Bob", "Carol"} +// result := Contains(names, "Bob") +// +// Result: +// +// true func Contains[T comparable](arr []T, target T) bool { for _, item := range arr { if item == target { @@ -9,3 +22,55 @@ func Contains[T comparable](arr []T, target T) bool { } return false } + +// GetJSONTags retrieves all JSON tag values from a struct. +// It returns a slice of JSON tag values extracted from the struct's fields. +// +// Example: +// +// type Person struct { +// ID int `json:"id"` +// Name string `json:"name"` +// Age int `json:"age"` +// CreatedAt string `json:"created_at"` +// } +// +// p := Person{} +// jsonTags := GetJSONTags(p) +// +// Result: +// +// ["id", "name", "age", "created_at"] +func GetJSONTags(v interface{}) []string { + var tags []string + val := reflect.ValueOf(v) + typ := val.Type() + + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + jsonTag := field.Tag.Get("json") + if jsonTag != "" { + tags = append(tags, jsonTag) + } + } + + return tags +} + +// GetMapKeys is a generic function that extracts all keys from a map and returns them in a slice. +// +// Example: +// +// ageMap := map[string]int{"Alice": 30, "Bob": 25, "Carol": 27} +// keys := GetMapKeys(ageMap) +// +// Result: +// +// ["Alice", "Bob", "Carol"] +func GetMapKeys[K comparable, V any](m map[K]V) []K { + var keys []K + for key := range m { + keys = append(keys, key) + } + return keys +} diff --git a/pkg/generic_helper/generic_helper_test.go b/pkg/generic_helper/generic_helper_test.go new file mode 100644 index 00000000..f8004b0c --- /dev/null +++ b/pkg/generic_helper/generic_helper_test.go @@ -0,0 +1,93 @@ +package generichelper_test + +import ( + "reflect" + "testing" + + generichelper "github.com/goto/compass/pkg/generic_helper" +) + +func TestContains(t *testing.T) { + tests := []struct { + name string + arr []string + target string + expected bool + }{ + {"Found", []string{"Alice", "Bob", "Carol"}, "Bob", true}, + {"Not Found", []string{"Alice", "Bob", "Carol"}, "Dave", false}, + {"Empty Array", []string{}, "Bob", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := generichelper.Contains(tt.arr, tt.target) + if result != tt.expected { + t.Errorf("Contains(%v, %v) = %v; want %v", tt.arr, tt.target, result, tt.expected) + } + }) + } +} + +func TestGetJSONTags(t *testing.T) { + type Person struct { + ID int `json:"id"` + Name string `json:"name"` + Age int `json:"age"` + CreatedAt string `json:"created_at"` + } + + p := Person{} + expectedTags := []string{"id", "name", "age", "created_at"} + + result := generichelper.GetJSONTags(p) + if !reflect.DeepEqual(result, expectedTags) { + t.Errorf("GetJSONTags(%v) = %v; want %v", p, result, expectedTags) + } +} + +func TestGetMapKeys(t *testing.T) { + tests := []struct { + name string + input map[string]int + expected []string + }{ + {"Simple Map", map[string]int{"Alice": 30, "Bob": 25, "Carol": 27}, []string{"Alice", "Bob", "Carol"}}, + {"Empty Map", map[string]int{}, nil}, + {"Single Element", map[string]int{"Alice": 30}, []string{"Alice"}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := generichelper.GetMapKeys(tt.input) + if !CompareSlices(result, tt.expected) { + t.Errorf("GetMapKeys(%v) = %v; want %v", tt.input, result, tt.expected) + } + }) + } +} + +// CompareSlices checks if two slices contain the same elements, regardless of order. +func CompareSlices[T comparable](a, b []T) bool { + if a == nil && b == nil { + return true + } + if len(a) != len(b) { + return false + } + + counts := make(map[T]int) + + for _, item := range a { + counts[item]++ + } + + for _, item := range b { + if counts[item] == 0 { + return false + } + counts[item]-- + } + + return true +} diff --git a/pkg/query_expr/delete_asset_expr.go b/pkg/query_expr/delete_asset_expr.go new file mode 100644 index 00000000..ec224628 --- /dev/null +++ b/pkg/query_expr/delete_asset_expr.go @@ -0,0 +1,49 @@ +package queryexpr + +import ( + "fmt" + + "github.com/goto/compass/core/asset" + generichelper "github.com/goto/compass/pkg/generic_helper" +) + +type DeleteAssetExpr struct { + ExprStr +} + +func (d DeleteAssetExpr) ToQuery() (string, error) { + return d.ExprStr.ToQuery() +} + +func (d DeleteAssetExpr) Validate() error { + identifiersWithOperator, err := GetIdentifiersMap(d.ExprStr.String()) + if err != nil { + return err + } + + isExist := func(jsonTag string) bool { + return identifiersWithOperator[jsonTag] != "" + } + mustExist := isExist("refreshed_at") && isExist("type") && isExist("service") + if !mustExist { + return fmt.Errorf("must exists these identifiers: refreshed_at, type, and service") + } + + getOperator := func(jsonTag string) string { + return identifiersWithOperator[jsonTag] + } + if getOperator("type") != "==" || getOperator("service") != "==" { + return fmt.Errorf("identifier type and service must be equals operator (==)") + } + + identifiers := generichelper.GetMapKeys(identifiersWithOperator) + jsonTagsSchema := generichelper.GetJSONTags(asset.Asset{}) + for _, identifier := range identifiers { + isFieldValid := generichelper.Contains(jsonTagsSchema, identifier) + if !isFieldValid { + return fmt.Errorf("%s is not a valid identifier", identifier) + } + } + + return nil +} diff --git a/pkg/query_expr/es_expr_test.go b/pkg/query_expr/es_expr_test.go index 93a2a75f..272c31c6 100644 --- a/pkg/query_expr/es_expr_test.go +++ b/pkg/query_expr/es_expr_test.go @@ -93,6 +93,5 @@ func TestESExpr_Validate(t *testing.T) { if err := (&expr).Validate(); err != nil { t.Errorf("Validate() error = %v, wantErr %v", err, nil) } - return }) } diff --git a/pkg/query_expr/es_test_data/equals-or-not-in-condition.json b/pkg/query_expr/es_test_data/equals-or-not-in-condition.json deleted file mode 100644 index 6942cafb..00000000 --- a/pkg/query_expr/es_test_data/equals-or-not-in-condition.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "query": { - "bool": { - "should": [ - { - "term": { - "name": "John" - } - }, - { - "bool": { - "must_not": [ - { - "terms": { - "service": [ - "test1", - "test2", - "test3" - ] - } - } - ] - } - } - ] - } - } -} \ No newline at end of file diff --git a/pkg/query_expr/es_test_data/in-condition.json b/pkg/query_expr/es_test_data/in-condition.json deleted file mode 100644 index 33fd2db7..00000000 --- a/pkg/query_expr/es_test_data/in-condition.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "query": { - "terms": { - "service": [ - "test1", - "test2", - "test3" - ] - } - } -} \ No newline at end of file diff --git a/pkg/query_expr/es_test_data/lt-condition.json b/pkg/query_expr/es_test_data/lt-condition.json deleted file mode 100644 index d29da73a..00000000 --- a/pkg/query_expr/es_test_data/lt-condition.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "query": { - "range": { - "updated_at": { - "lt": "2024-04-05 23:59:59" - } - } - } -} \ No newline at end of file diff --git a/pkg/query_expr/query_expr.go b/pkg/query_expr/query_expr.go index cbddcbfd..bb471c05 100644 --- a/pkg/query_expr/query_expr.go +++ b/pkg/query_expr/query_expr.go @@ -9,12 +9,13 @@ import ( ) type ExprStr interface { + String() string ToQuery() (string, error) Validate() error } type ExprVisitor struct { - Identifiers []string + IdentifiersWithOperator map[string]string // Key: Identifier, Value: Operator } type ExprParam map[string]interface{} @@ -33,19 +34,24 @@ func ValidateAndGetQueryFromExpr(exprStr ExprStr) (string, error) { // Visit is implementation Visitor interface from expr-lang/expr lib, used by ast.Walk func (s *ExprVisitor) Visit(node *ast.Node) { //nolint:gocritic - if n, ok := (*node).(*ast.IdentifierNode); ok { - s.Identifiers = append(s.Identifiers, n.Value) + if n, ok := (*node).(*ast.BinaryNode); ok { + if left, ok := (n.Left).(*ast.IdentifierNode); ok { + s.IdentifiersWithOperator[left.Value] = n.Operator + } + if right, ok := (n.Right).(*ast.IdentifierNode); ok { + s.IdentifiersWithOperator[right.Value] = n.Operator + } } } -func GetIdentifiers(queryExpr string) ([]string, error) { +func GetIdentifiersMap(queryExpr string) (map[string]string, error) { queryExprParsed, err := GetTreeNodeFromQueryExpr(queryExpr) if err != nil { return nil, err } - queryExprVisitor := &ExprVisitor{} + queryExprVisitor := &ExprVisitor{IdentifiersWithOperator: make(map[string]string)} ast.Walk(&queryExprParsed, queryExprVisitor) - return queryExprVisitor.Identifiers, nil + return queryExprVisitor.IdentifiersWithOperator, nil } func GetTreeNodeFromQueryExpr(queryExpr string) (ast.Node, error) { diff --git a/pkg/query_expr/query_expr_test.go b/pkg/query_expr/query_expr_test.go index 748628fa..f7d21599 100644 --- a/pkg/query_expr/query_expr_test.go +++ b/pkg/query_expr/query_expr_test.go @@ -1,33 +1,41 @@ -package queryexpr +package queryexpr_test import ( "reflect" "testing" + + queryexpr "github.com/goto/compass/pkg/query_expr" ) -func TestGetIdentifiers(t *testing.T) { +func TestGetIdentifiersMap(t *testing.T) { tests := []struct { name string expr string - want []string + want map[string]string wantErr bool }{ { name: "got 0 identifier test", expr: `findLast([1, 2, 3, 4], # > 2)`, - want: nil, + want: map[string]string{}, wantErr: false, }, { - name: "got 1 identifiers test", - expr: `updated_at < '2024-04-05 23:59:59'`, - want: []string{"updated_at"}, + name: "got 1 identifiers test", + expr: `updated_at < '2024-04-05 23:59:59'`, + want: map[string]string{ + "updated_at": "<", + }, wantErr: false, }, { - name: "got 3 identifiers test", - expr: `(identifier1 == !(findLast([1, 2, 3, 4], # > 2) == 4)) && identifier2 != 'John' || identifier3 == "hallo"`, - want: []string{"identifier1", "identifier2", "identifier3"}, + name: "got 3 identifiers test", + expr: `(identifier1 == !(findLast([1, 2, 3, 4], # > 2) == 4)) && identifier2 != 'John' || identifier3 == "hallo"`, + want: map[string]string{ + "identifier1": "==", + "identifier2": "!=", + "identifier3": "==", + }, wantErr: false, }, { @@ -39,13 +47,13 @@ func TestGetIdentifiers(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetIdentifiers(tt.expr) + got, err := queryexpr.GetIdentifiersMap(tt.expr) if (err != nil) != tt.wantErr { - t.Errorf("GetIdentifiers() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("GetIdentifiersMap() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("GetIdentifiers() got = %v, want %v", got, tt.want) + t.Errorf("GetIdentifiersMap() got = %v, want %v", got, tt.want) } }) } @@ -79,7 +87,7 @@ func TestGetQueryExprResult(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetQueryExprResult(tt.expr) + got, err := queryexpr.GetQueryExprResult(tt.expr) if (err != nil) != tt.wantErr { t.Errorf("GetQueryExprResult() error = %v, wantErr %v", err, tt.wantErr) return @@ -119,7 +127,7 @@ func TestGetTreeNodeFromQueryExpr(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetTreeNodeFromQueryExpr(tt.expr) + got, err := queryexpr.GetTreeNodeFromQueryExpr(tt.expr) if (err != nil) != tt.wantErr { t.Errorf("GetTreeNodeFromQueryExpr() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/pkg/query_expr/sql_expr.go b/pkg/query_expr/sql_expr.go index ec5e3c52..6fecc196 100644 --- a/pkg/query_expr/sql_expr.go +++ b/pkg/query_expr/sql_expr.go @@ -127,14 +127,15 @@ func (s *SQLExpr) patchUnaryNode(n *ast.UnaryNode) error { switch n.Operator { case "not": binaryNode, ok := (n.Node).(*ast.BinaryNode) - if ok && strings.ToUpper(binaryNode.Operator) == "IN" { + if !ok { + return s.unsupportedQueryError(n) + } + if strings.ToUpper(binaryNode.Operator) == "IN" { ast.Patch(&n.Node, &ast.BinaryNode{ Operator: "not in", Left: binaryNode.Left, Right: binaryNode.Right, }) - } else { - return s.unsupportedQueryError(n) } case "!": switch nodeV := n.Node.(type) { diff --git a/pkg/query_expr/sql_expr_test.go b/pkg/query_expr/sql_expr_test.go index fa4eb52e..16728676 100644 --- a/pkg/query_expr/sql_expr_test.go +++ b/pkg/query_expr/sql_expr_test.go @@ -93,6 +93,5 @@ func TestSQLExpr_Validate(t *testing.T) { if err := (&expr).Validate(); err != nil { t.Errorf("Validate() error = %v, wantErr %v", err, nil) } - return }) } From b7afadc4b0805f5bd859f9993c60817dd8521062 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Tue, 13 Aug 2024 11:37:42 +0700 Subject: [PATCH 29/45] feat: make can equals (==) or IN for type and service in delete asset expr --- pkg/query_expr/delete_asset_expr.go | 9 +++++---- pkg/query_expr/query_expr.go | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/pkg/query_expr/delete_asset_expr.go b/pkg/query_expr/delete_asset_expr.go index ec224628..cdcc12d3 100644 --- a/pkg/query_expr/delete_asset_expr.go +++ b/pkg/query_expr/delete_asset_expr.go @@ -2,6 +2,7 @@ package queryexpr import ( "fmt" + "strings" "github.com/goto/compass/core/asset" generichelper "github.com/goto/compass/pkg/generic_helper" @@ -29,11 +30,11 @@ func (d DeleteAssetExpr) Validate() error { return fmt.Errorf("must exists these identifiers: refreshed_at, type, and service") } - getOperator := func(jsonTag string) string { - return identifiersWithOperator[jsonTag] + isOperatorEqualsOrIn := func(jsonTag string) bool { + return identifiersWithOperator[jsonTag] == "==" || strings.ToUpper(identifiersWithOperator[jsonTag]) == "IN" } - if getOperator("type") != "==" || getOperator("service") != "==" { - return fmt.Errorf("identifier type and service must be equals operator (==)") + if !isOperatorEqualsOrIn("type") || !isOperatorEqualsOrIn("service") { + return fmt.Errorf("identifier type and service must be equals (==) or IN operator") } identifiers := generichelper.GetMapKeys(identifiersWithOperator) diff --git a/pkg/query_expr/query_expr.go b/pkg/query_expr/query_expr.go index bb471c05..7680efd4 100644 --- a/pkg/query_expr/query_expr.go +++ b/pkg/query_expr/query_expr.go @@ -2,6 +2,7 @@ package queryexpr import ( "fmt" + "strings" "github.com/expr-lang/expr" "github.com/expr-lang/expr/ast" @@ -34,13 +35,26 @@ func ValidateAndGetQueryFromExpr(exprStr ExprStr) (string, error) { // Visit is implementation Visitor interface from expr-lang/expr lib, used by ast.Walk func (s *ExprVisitor) Visit(node *ast.Node) { //nolint:gocritic - if n, ok := (*node).(*ast.BinaryNode); ok { + switch n := (*node).(type) { + case *ast.BinaryNode: if left, ok := (n.Left).(*ast.IdentifierNode); ok { s.IdentifiersWithOperator[left.Value] = n.Operator } if right, ok := (n.Right).(*ast.IdentifierNode); ok { s.IdentifiersWithOperator[right.Value] = n.Operator } + case *ast.UnaryNode: + if binaryNode, ok := (n.Node).(*ast.BinaryNode); ok { + if strings.ToUpper(binaryNode.Operator) == "IN" { + notInOperator := "NOT IN" + if left, ok := (binaryNode.Left).(*ast.IdentifierNode); ok { + s.IdentifiersWithOperator[left.Value] = notInOperator + } + if right, ok := (binaryNode.Right).(*ast.IdentifierNode); ok { + s.IdentifiersWithOperator[right.Value] = notInOperator + } + } + } } } From 4abd20d30db589f40a6262d2c8626273c5437261 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Tue, 13 Aug 2024 11:38:01 +0700 Subject: [PATCH 30/45] test: create unit test for delete asset expr --- pkg/query_expr/delete_asset_expr_test.go | 149 +++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 pkg/query_expr/delete_asset_expr_test.go diff --git a/pkg/query_expr/delete_asset_expr_test.go b/pkg/query_expr/delete_asset_expr_test.go new file mode 100644 index 00000000..43f2e368 --- /dev/null +++ b/pkg/query_expr/delete_asset_expr_test.go @@ -0,0 +1,149 @@ +package queryexpr_test + +import ( + "errors" + "testing" + + queryexpr "github.com/goto/compass/pkg/query_expr" + "github.com/stretchr/testify/assert" +) + +func TestDeleteAssetExpr_ToQuery(t *testing.T) { + queryExp := `name == "John" || service not in ["test1","test2","test3"]` + sqlExpr := queryexpr.SQLExpr(queryExp) + esExpr := queryexpr.ESExpr(queryExp) + wrongExpr := queryexpr.SQLExpr("findLast(") + tests := []struct { + name string + exprStr queryexpr.ExprStr + want string + wantErr bool + }{ + { + name: "convert to SQL query", + exprStr: queryexpr.DeleteAssetExpr{ + ExprStr: &sqlExpr, + }, + want: "((name = 'John') OR (service NOT IN ('test1', 'test2', 'test3')))", + wantErr: false, + }, + { + name: "convert to ES query", + exprStr: queryexpr.DeleteAssetExpr{ + ExprStr: &esExpr, + }, + want: `{"query":{"bool":{"should":[{"term":{"name":"John"}},{"bool":{"must_not":[{"terms":{"service":["test1","test2","test3"]}}]}}]}}}`, + wantErr: false, + }, + { + name: "got error due to wrong syntax", + exprStr: queryexpr.DeleteAssetExpr{ + ExprStr: &wrongExpr, + }, + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := queryexpr.DeleteAssetExpr{ + ExprStr: tt.exprStr, + } + got, err := d.ToQuery() + if (err != nil) != tt.wantErr { + t.Errorf("ToQuery() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ToQuery() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDeleteAssetExpr_Validate(t *testing.T) { + tests := []struct { + name string + exprStrFn func() queryexpr.ExprStr + expectErr error + wantErr bool + }{ + { + name: "error get identifiers map", + exprStrFn: func() queryexpr.ExprStr { + wrongExpr := queryexpr.SQLExpr("findLast(") + return queryexpr.DeleteAssetExpr{ + ExprStr: &wrongExpr, + } + }, + expectErr: errors.New("error parsing expression"), + wantErr: true, + }, + { + name: "error miss refreshed_at not exist", + exprStrFn: func() queryexpr.ExprStr { + missRefreshedAt := queryexpr.SQLExpr(`updated_at < "2023-12-12 23:59:59" && type == "table" && service in ["test1","test2","test3"]`) + return queryexpr.DeleteAssetExpr{ + ExprStr: &missRefreshedAt, + } + }, + expectErr: errors.New("must exists these identifiers: refreshed_at, type, and service"), + wantErr: true, + }, + { + name: "error miss type not exist", + exprStrFn: func() queryexpr.ExprStr { + missType := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && service in ["test1","test2","test3"]`) + return queryexpr.DeleteAssetExpr{ + ExprStr: &missType, + } + }, + expectErr: errors.New("must exists these identifiers: refreshed_at, type, and service"), + wantErr: true, + }, + { + name: "error miss service not exist", + exprStrFn: func() queryexpr.ExprStr { + missService := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && type == "table"`) + return queryexpr.DeleteAssetExpr{ + ExprStr: &missService, + } + }, + expectErr: errors.New("must exists these identifiers: refreshed_at, type, and service"), + wantErr: true, + }, + { + name: "error wrong operator for type identifier", + exprStrFn: func() queryexpr.ExprStr { + wrongTypeOperator := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && type != "table" && service in ["test1","test2","test3"]`) + return queryexpr.DeleteAssetExpr{ + ExprStr: &wrongTypeOperator, + } + }, + expectErr: errors.New("identifier type and service must be equals (==) or IN operator"), + wantErr: true, + }, + { + name: "error wrong operator for service identifier", + exprStrFn: func() queryexpr.ExprStr { + wrongServiceOperator := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && type != "table" && service not in ["test1","test2","test3"]`) + return queryexpr.DeleteAssetExpr{ + ExprStr: &wrongServiceOperator, + } + }, + expectErr: errors.New("identifier type and service must be equals (==) or IN operator"), + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.exprStrFn().Validate() + if (err != nil) != tt.wantErr { + t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + assert.ErrorContains(t, err, tt.expectErr.Error()) + } + }) + } +} From a77cf726c654f027903620ee059fcada73870634 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Tue, 20 Aug 2024 13:25:16 +0700 Subject: [PATCH 31/45] feat: change flow of deletion assets and refactor codes based on feedbacks --- core/asset/asset.go | 7 +- .../asset}/delete_asset_expr.go | 52 ++++-- .../asset}/delete_asset_expr_test.go | 25 +-- core/asset/delete_assets.go | 6 + core/asset/discovery.go | 2 +- core/asset/errors.go | 2 +- core/asset/mocks/asset_repository.go | 51 +++--- core/asset/mocks/discovery_repository.go | 16 +- core/asset/service.go | 53 +++---- core/asset/service_test.go | 40 ++--- internal/server/v1beta1/asset.go | 14 +- internal/server/v1beta1/asset_test.go | 38 ++--- .../server/v1beta1/mocks/asset_service.go | 31 ++-- .../elasticsearch/discovery_repository.go | 7 +- .../discovery_repository_test.go | 4 +- internal/store/postgres/asset_model.go | 28 ++-- internal/store/postgres/asset_repository.go | 148 ++++++------------ .../store/postgres/asset_repository_test.go | 21 ++- internal/workermanager/discovery_worker.go | 11 +- .../workermanager/discovery_worker_test.go | 12 +- internal/workermanager/job_types.go | 8 +- internal/workermanager/worker_manager.go | 8 +- internal/workermanager/worker_manager_test.go | 8 +- pkg/{query_expr => queryexpr}/es_expr.go | 20 ++- pkg/{query_expr => queryexpr}/es_expr_test.go | 2 +- pkg/{query_expr => queryexpr}/query_expr.go | 4 +- .../query_expr_test.go | 44 +----- pkg/{query_expr => queryexpr}/sql_expr.go | 2 +- .../sql_expr_test.go | 2 +- 29 files changed, 294 insertions(+), 372 deletions(-) rename {pkg/query_expr => core/asset}/delete_asset_expr.go (55%) rename {pkg/query_expr => core/asset}/delete_asset_expr_test.go (89%) create mode 100644 core/asset/delete_assets.go rename pkg/{query_expr => queryexpr}/es_expr.go (92%) rename pkg/{query_expr => queryexpr}/es_expr_test.go (98%) rename pkg/{query_expr => queryexpr}/query_expr.go (94%) rename pkg/{query_expr => queryexpr}/query_expr_test.go (68%) rename pkg/{query_expr => queryexpr}/sql_expr.go (98%) rename pkg/{query_expr => queryexpr}/sql_expr_test.go (98%) diff --git a/core/asset/asset.go b/core/asset/asset.go index 794f336a..85e1f342 100644 --- a/core/asset/asset.go +++ b/core/asset/asset.go @@ -6,13 +6,14 @@ import ( "time" "github.com/goto/compass/core/user" + "github.com/goto/compass/pkg/queryexpr" "github.com/r3labs/diff/v2" ) type Repository interface { GetAll(context.Context, Filter) ([]Asset, error) GetCount(context.Context, Filter) (int, error) - GetCountByQueryExpr(context.Context, string, bool) (int, error) + GetCountByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) (int, error) GetByID(ctx context.Context, id string) (Asset, error) GetByURN(ctx context.Context, urn string) (Asset, error) GetVersionHistory(ctx context.Context, flt Filter, id string) ([]Asset, error) @@ -22,7 +23,7 @@ type Repository interface { Upsert(ctx context.Context, ast *Asset) (string, error) DeleteByID(ctx context.Context, id string) error DeleteByURN(ctx context.Context, urn string) error - DeleteByQueryExpr(ctx context.Context, queryExpr string) ([]string, error) + DeleteByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) ([]string, error) AddProbe(ctx context.Context, assetURN string, probe *Probe) error GetProbes(ctx context.Context, assetURN string) ([]Probe, error) GetProbesWithFilter(ctx context.Context, flt ProbesFilter) (map[string][]Probe, error) @@ -42,7 +43,7 @@ type Asset struct { Owners []user.User `json:"owners,omitempty" diff:"owners"` CreatedAt time.Time `json:"created_at" diff:"-"` UpdatedAt time.Time `json:"updated_at" diff:"-"` - RefreshedAt time.Time `json:"refreshed_at" diff:"-"` + RefreshedAt *time.Time `json:"refreshed_at" diff:"-"` Version string `json:"version" diff:"-"` UpdatedBy user.User `json:"updated_by" diff:"-"` Changelog diff.Changelog `json:"changelog,omitempty" diff:"-"` diff --git a/pkg/query_expr/delete_asset_expr.go b/core/asset/delete_asset_expr.go similarity index 55% rename from pkg/query_expr/delete_asset_expr.go rename to core/asset/delete_asset_expr.go index cdcc12d3..9d55f279 100644 --- a/pkg/query_expr/delete_asset_expr.go +++ b/core/asset/delete_asset_expr.go @@ -1,15 +1,17 @@ -package queryexpr +package asset import ( "fmt" "strings" - "github.com/goto/compass/core/asset" generichelper "github.com/goto/compass/pkg/generic_helper" + "github.com/goto/compass/pkg/queryexpr" ) +var assetJSONTagsSchema = generichelper.GetJSONTags(Asset{}) + type DeleteAssetExpr struct { - ExprStr + queryexpr.ExprStr } func (d DeleteAssetExpr) ToQuery() (string, error) { @@ -17,34 +19,50 @@ func (d DeleteAssetExpr) ToQuery() (string, error) { } func (d DeleteAssetExpr) Validate() error { - identifiersWithOperator, err := GetIdentifiersMap(d.ExprStr.String()) + identifiersWithOperator, err := queryexpr.GetIdentifiersMap(d.ExprStr.String()) if err != nil { return err } - isExist := func(jsonTag string) bool { - return identifiersWithOperator[jsonTag] != "" - } - mustExist := isExist("refreshed_at") && isExist("type") && isExist("service") - if !mustExist { - return fmt.Errorf("must exists these identifiers: refreshed_at, type, and service") + if err := d.isRequiredIdentifiersExist(identifiersWithOperator); err != nil { + return err } - isOperatorEqualsOrIn := func(jsonTag string) bool { - return identifiersWithOperator[jsonTag] == "==" || strings.ToUpper(identifiersWithOperator[jsonTag]) == "IN" - } - if !isOperatorEqualsOrIn("type") || !isOperatorEqualsOrIn("service") { - return fmt.Errorf("identifier type and service must be equals (==) or IN operator") + if err := d.isUsingRightOperator(identifiersWithOperator); err != nil { + return err } + return d.isAllIdentifiersExistInStruct(identifiersWithOperator) +} + +func (DeleteAssetExpr) isAllIdentifiersExistInStruct(identifiersWithOperator map[string]string) error { identifiers := generichelper.GetMapKeys(identifiersWithOperator) - jsonTagsSchema := generichelper.GetJSONTags(asset.Asset{}) for _, identifier := range identifiers { - isFieldValid := generichelper.Contains(jsonTagsSchema, identifier) + isFieldValid := generichelper.Contains(assetJSONTagsSchema, identifier) if !isFieldValid { return fmt.Errorf("%s is not a valid identifier", identifier) } } + return nil +} + +func (DeleteAssetExpr) isUsingRightOperator(identifiersWithOperator map[string]string) error { + isOperatorEqualsOrIn := func(jsonTag string) bool { + return identifiersWithOperator[jsonTag] == "==" || strings.ToUpper(identifiersWithOperator[jsonTag]) == "IN" + } + if !isOperatorEqualsOrIn("type") || !isOperatorEqualsOrIn("service") { + return fmt.Errorf("identifier type and service must be equals (==) or IN operator") + } + return nil +} +func (DeleteAssetExpr) isRequiredIdentifiersExist(identifiersWithOperator map[string]string) error { + isExist := func(jsonTag string) bool { + return identifiersWithOperator[jsonTag] != "" + } + mustExist := isExist("refreshed_at") && isExist("type") && isExist("service") + if !mustExist { + return fmt.Errorf("must exists these identifiers: refreshed_at, type, and service") + } return nil } diff --git a/pkg/query_expr/delete_asset_expr_test.go b/core/asset/delete_asset_expr_test.go similarity index 89% rename from pkg/query_expr/delete_asset_expr_test.go rename to core/asset/delete_asset_expr_test.go index 43f2e368..5332acf0 100644 --- a/pkg/query_expr/delete_asset_expr_test.go +++ b/core/asset/delete_asset_expr_test.go @@ -1,10 +1,11 @@ -package queryexpr_test +package asset_test import ( "errors" "testing" - queryexpr "github.com/goto/compass/pkg/query_expr" + "github.com/goto/compass/core/asset" + "github.com/goto/compass/pkg/queryexpr" "github.com/stretchr/testify/assert" ) @@ -21,7 +22,7 @@ func TestDeleteAssetExpr_ToQuery(t *testing.T) { }{ { name: "convert to SQL query", - exprStr: queryexpr.DeleteAssetExpr{ + exprStr: asset.DeleteAssetExpr{ ExprStr: &sqlExpr, }, want: "((name = 'John') OR (service NOT IN ('test1', 'test2', 'test3')))", @@ -29,7 +30,7 @@ func TestDeleteAssetExpr_ToQuery(t *testing.T) { }, { name: "convert to ES query", - exprStr: queryexpr.DeleteAssetExpr{ + exprStr: asset.DeleteAssetExpr{ ExprStr: &esExpr, }, want: `{"query":{"bool":{"should":[{"term":{"name":"John"}},{"bool":{"must_not":[{"terms":{"service":["test1","test2","test3"]}}]}}]}}}`, @@ -37,7 +38,7 @@ func TestDeleteAssetExpr_ToQuery(t *testing.T) { }, { name: "got error due to wrong syntax", - exprStr: queryexpr.DeleteAssetExpr{ + exprStr: asset.DeleteAssetExpr{ ExprStr: &wrongExpr, }, want: "", @@ -46,7 +47,7 @@ func TestDeleteAssetExpr_ToQuery(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - d := queryexpr.DeleteAssetExpr{ + d := asset.DeleteAssetExpr{ ExprStr: tt.exprStr, } got, err := d.ToQuery() @@ -72,7 +73,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { name: "error get identifiers map", exprStrFn: func() queryexpr.ExprStr { wrongExpr := queryexpr.SQLExpr("findLast(") - return queryexpr.DeleteAssetExpr{ + return asset.DeleteAssetExpr{ ExprStr: &wrongExpr, } }, @@ -83,7 +84,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { name: "error miss refreshed_at not exist", exprStrFn: func() queryexpr.ExprStr { missRefreshedAt := queryexpr.SQLExpr(`updated_at < "2023-12-12 23:59:59" && type == "table" && service in ["test1","test2","test3"]`) - return queryexpr.DeleteAssetExpr{ + return asset.DeleteAssetExpr{ ExprStr: &missRefreshedAt, } }, @@ -94,7 +95,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { name: "error miss type not exist", exprStrFn: func() queryexpr.ExprStr { missType := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && service in ["test1","test2","test3"]`) - return queryexpr.DeleteAssetExpr{ + return asset.DeleteAssetExpr{ ExprStr: &missType, } }, @@ -105,7 +106,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { name: "error miss service not exist", exprStrFn: func() queryexpr.ExprStr { missService := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && type == "table"`) - return queryexpr.DeleteAssetExpr{ + return asset.DeleteAssetExpr{ ExprStr: &missService, } }, @@ -116,7 +117,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { name: "error wrong operator for type identifier", exprStrFn: func() queryexpr.ExprStr { wrongTypeOperator := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && type != "table" && service in ["test1","test2","test3"]`) - return queryexpr.DeleteAssetExpr{ + return asset.DeleteAssetExpr{ ExprStr: &wrongTypeOperator, } }, @@ -127,7 +128,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { name: "error wrong operator for service identifier", exprStrFn: func() queryexpr.ExprStr { wrongServiceOperator := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && type != "table" && service not in ["test1","test2","test3"]`) - return queryexpr.DeleteAssetExpr{ + return asset.DeleteAssetExpr{ ExprStr: &wrongServiceOperator, } }, diff --git a/core/asset/delete_assets.go b/core/asset/delete_assets.go new file mode 100644 index 00000000..e87c100c --- /dev/null +++ b/core/asset/delete_assets.go @@ -0,0 +1,6 @@ +package asset + +type DeleteAssetsRequest struct { + QueryExpr string + DryRun bool +} diff --git a/core/asset/discovery.go b/core/asset/discovery.go index c7cbeef2..e9ad90b1 100644 --- a/core/asset/discovery.go +++ b/core/asset/discovery.go @@ -9,7 +9,7 @@ type DiscoveryRepository interface { Upsert(context.Context, Asset) error DeleteByID(ctx context.Context, assetID string) error DeleteByURN(ctx context.Context, assetURN string) error - DeleteByQueryExpr(ctx context.Context, filterQuery string) error + DeleteByQueryExpr(ctx context.Context, queryExpr string) error Search(ctx context.Context, cfg SearchConfig) (results []SearchResult, err error) Suggest(ctx context.Context, cfg SearchConfig) (suggestions []string, err error) GroupAssets(ctx context.Context, cfg GroupConfig) (results []GroupResult, err error) diff --git a/core/asset/errors.go b/core/asset/errors.go index 26d03cd7..7fbd8fb1 100644 --- a/core/asset/errors.go +++ b/core/asset/errors.go @@ -10,7 +10,7 @@ var ( ErrEmptyID = errors.New("asset does not have ID") ErrProbeExists = errors.New("asset probe already exists") ErrEmptyURN = errors.New("asset does not have URN") - ErrEmptyQuery = errors.New("query must exist to filtering assets") + ErrEmptyQuery = errors.New("query is empty") ErrUnknownType = errors.New("unknown type") ErrNilAsset = errors.New("nil asset") ) diff --git a/core/asset/mocks/asset_repository.go b/core/asset/mocks/asset_repository.go index b48724b7..aa7be194 100644 --- a/core/asset/mocks/asset_repository.go +++ b/core/asset/mocks/asset_repository.go @@ -8,6 +8,8 @@ import ( asset "github.com/goto/compass/core/asset" mock "github.com/stretchr/testify/mock" + + queryexpr "github.com/goto/compass/pkg/queryexpr" ) // AssetRepository is an autogenerated mock type for the Repository type @@ -111,15 +113,15 @@ func (_c *AssetRepository_DeleteByID_Call) RunAndReturn(run func(context.Context } // DeleteByQueryExpr provides a mock function with given fields: ctx, queryExpr -func (_m *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) ([]string, error) { +func (_m *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) ([]string, error) { ret := _m.Called(ctx, queryExpr) var r0 []string var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) ([]string, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, queryexpr.ExprStr) ([]string, error)); ok { return rf(ctx, queryExpr) } - if rf, ok := ret.Get(0).(func(context.Context, string) []string); ok { + if rf, ok := ret.Get(0).(func(context.Context, queryexpr.ExprStr) []string); ok { r0 = rf(ctx, queryExpr) } else { if ret.Get(0) != nil { @@ -127,7 +129,7 @@ func (_m *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr stri } } - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, queryexpr.ExprStr) error); ok { r1 = rf(ctx, queryExpr) } else { r1 = ret.Error(1) @@ -143,14 +145,14 @@ type AssetRepository_DeleteByQueryExpr_Call struct { // DeleteByQueryExpr is a helper method to define mock.On call // - ctx context.Context -// - queryExpr string +// - queryExpr queryexpr.ExprStr func (_e *AssetRepository_Expecter) DeleteByQueryExpr(ctx interface{}, queryExpr interface{}) *AssetRepository_DeleteByQueryExpr_Call { return &AssetRepository_DeleteByQueryExpr_Call{Call: _e.mock.On("DeleteByQueryExpr", ctx, queryExpr)} } -func (_c *AssetRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, queryExpr string)) *AssetRepository_DeleteByQueryExpr_Call { +func (_c *AssetRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, queryExpr queryexpr.ExprStr)) *AssetRepository_DeleteByQueryExpr_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) + run(args[0].(context.Context), args[1].(queryexpr.ExprStr)) }) return _c } @@ -160,7 +162,7 @@ func (_c *AssetRepository_DeleteByQueryExpr_Call) Return(_a0 []string, _a1 error return _c } -func (_c *AssetRepository_DeleteByQueryExpr_Call) RunAndReturn(run func(context.Context, string) ([]string, error)) *AssetRepository_DeleteByQueryExpr_Call { +func (_c *AssetRepository_DeleteByQueryExpr_Call) RunAndReturn(run func(context.Context, queryexpr.ExprStr) ([]string, error)) *AssetRepository_DeleteByQueryExpr_Call { _c.Call.Return(run) return _c } @@ -530,23 +532,23 @@ func (_c *AssetRepository_GetCount_Call) RunAndReturn(run func(context.Context, return _c } -// GetCountByQueryExpr provides a mock function with given fields: _a0, _a1, _a2 -func (_m *AssetRepository) GetCountByQueryExpr(_a0 context.Context, _a1 string, _a2 bool) (int, error) { - ret := _m.Called(_a0, _a1, _a2) +// GetCountByQueryExpr provides a mock function with given fields: ctx, queryExpr +func (_m *AssetRepository) GetCountByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) (int, error) { + ret := _m.Called(ctx, queryExpr) var r0 int var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, bool) (int, error)); ok { - return rf(_a0, _a1, _a2) + if rf, ok := ret.Get(0).(func(context.Context, queryexpr.ExprStr) (int, error)); ok { + return rf(ctx, queryExpr) } - if rf, ok := ret.Get(0).(func(context.Context, string, bool) int); ok { - r0 = rf(_a0, _a1, _a2) + if rf, ok := ret.Get(0).(func(context.Context, queryexpr.ExprStr) int); ok { + r0 = rf(ctx, queryExpr) } else { r0 = ret.Get(0).(int) } - if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { - r1 = rf(_a0, _a1, _a2) + if rf, ok := ret.Get(1).(func(context.Context, queryexpr.ExprStr) error); ok { + r1 = rf(ctx, queryExpr) } else { r1 = ret.Error(1) } @@ -560,16 +562,15 @@ type AssetRepository_GetCountByQueryExpr_Call struct { } // GetCountByQueryExpr is a helper method to define mock.On call -// - _a0 context.Context -// - _a1 string -// - _a2 bool -func (_e *AssetRepository_Expecter) GetCountByQueryExpr(_a0 interface{}, _a1 interface{}, _a2 interface{}) *AssetRepository_GetCountByQueryExpr_Call { - return &AssetRepository_GetCountByQueryExpr_Call{Call: _e.mock.On("GetCountByQueryExpr", _a0, _a1, _a2)} +// - ctx context.Context +// - queryExpr queryexpr.ExprStr +func (_e *AssetRepository_Expecter) GetCountByQueryExpr(ctx interface{}, queryExpr interface{}) *AssetRepository_GetCountByQueryExpr_Call { + return &AssetRepository_GetCountByQueryExpr_Call{Call: _e.mock.On("GetCountByQueryExpr", ctx, queryExpr)} } -func (_c *AssetRepository_GetCountByQueryExpr_Call) Run(run func(_a0 context.Context, _a1 string, _a2 bool)) *AssetRepository_GetCountByQueryExpr_Call { +func (_c *AssetRepository_GetCountByQueryExpr_Call) Run(run func(ctx context.Context, queryExpr queryexpr.ExprStr)) *AssetRepository_GetCountByQueryExpr_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(bool)) + run(args[0].(context.Context), args[1].(queryexpr.ExprStr)) }) return _c } @@ -579,7 +580,7 @@ func (_c *AssetRepository_GetCountByQueryExpr_Call) Return(_a0 int, _a1 error) * return _c } -func (_c *AssetRepository_GetCountByQueryExpr_Call) RunAndReturn(run func(context.Context, string, bool) (int, error)) *AssetRepository_GetCountByQueryExpr_Call { +func (_c *AssetRepository_GetCountByQueryExpr_Call) RunAndReturn(run func(context.Context, queryexpr.ExprStr) (int, error)) *AssetRepository_GetCountByQueryExpr_Call { _c.Call.Return(run) return _c } diff --git a/core/asset/mocks/discovery_repository.go b/core/asset/mocks/discovery_repository.go index 00627ed1..465c31d3 100644 --- a/core/asset/mocks/discovery_repository.go +++ b/core/asset/mocks/discovery_repository.go @@ -66,13 +66,13 @@ func (_c *DiscoveryRepository_DeleteByID_Call) RunAndReturn(run func(context.Con return _c } -// DeleteByQueryExpr provides a mock function with given fields: ctx, filterQuery -func (_m *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, filterQuery string) error { - ret := _m.Called(ctx, filterQuery) +// DeleteByQueryExpr provides a mock function with given fields: ctx, queryExpr +func (_m *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) error { + ret := _m.Called(ctx, queryExpr) var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { - r0 = rf(ctx, filterQuery) + r0 = rf(ctx, queryExpr) } else { r0 = ret.Error(0) } @@ -87,12 +87,12 @@ type DiscoveryRepository_DeleteByQueryExpr_Call struct { // DeleteByQueryExpr is a helper method to define mock.On call // - ctx context.Context -// - filterQuery string -func (_e *DiscoveryRepository_Expecter) DeleteByQueryExpr(ctx interface{}, filterQuery interface{}) *DiscoveryRepository_DeleteByQueryExpr_Call { - return &DiscoveryRepository_DeleteByQueryExpr_Call{Call: _e.mock.On("DeleteByQueryExpr", ctx, filterQuery)} +// - queryExpr string +func (_e *DiscoveryRepository_Expecter) DeleteByQueryExpr(ctx interface{}, queryExpr interface{}) *DiscoveryRepository_DeleteByQueryExpr_Call { + return &DiscoveryRepository_DeleteByQueryExpr_Call{Call: _e.mock.On("DeleteByQueryExpr", ctx, queryExpr)} } -func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, filterQuery string)) *DiscoveryRepository_DeleteByQueryExpr_Call { +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, queryExpr string)) *DiscoveryRepository_DeleteByQueryExpr_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(string)) }) diff --git a/core/asset/service.go b/core/asset/service.go index cfffb359..0ed3e7d7 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -4,10 +4,10 @@ import ( "context" "fmt" "log" - "sync" "time" "github.com/google/uuid" + "github.com/goto/compass/pkg/queryexpr" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -87,8 +87,8 @@ func (s *Service) UpsertAsset(ctx context.Context, ast *Asset, upstreams, downst } func (s *Service) UpsertAssetWithoutLineage(ctx context.Context, ast *Asset) (string, error) { - // Update the asset in both postgresql and elasticsearch for each upsert - ast.RefreshedAt = time.Now() + currentTime := time.Now() + ast.RefreshedAt = ¤tTime assetID, err := s.assetRepository.Upsert(ctx, ast) if err != nil { @@ -129,46 +129,41 @@ func (s *Service) DeleteAsset(ctx context.Context, id string) (err error) { return s.lineageRepository.DeleteByURN(ctx, urn) } -func (s *Service) DeleteAssets(ctx context.Context, queryExpr string, dryRun bool) (affectedRows uint32, err error) { - var wg sync.WaitGroup - var urns []string - dbErrChan := make(chan error, 1) - - total, err := s.assetRepository.GetCountByQueryExpr(ctx, queryExpr, true) +func (s *Service) DeleteAssets(ctx context.Context, request DeleteAssetsRequest) (affectedRows uint32, err error) { + expr := queryexpr.SQLExpr(request.QueryExpr) + deleteExpr := &DeleteAssetExpr{ + ExprStr: &expr, + } + total, err := s.assetRepository.GetCountByQueryExpr(ctx, deleteExpr) if err != nil { return 0, err } - if dryRun { + if request.DryRun { return uint32(total), nil } - s.deleteAssetsAsynchronously(&wg, queryExpr, urns, dbErrChan) + s.deleteAssetsAsynchronously(request.QueryExpr) return uint32(total), nil } -func (s *Service) deleteAssetsAsynchronously(wg *sync.WaitGroup, queryExpr string, urns []string, dbErrChan chan error) { - // Perform the Assets deletion asynchronously - wg.Add(1) - go func() { - defer wg.Done() - urnsDeleted, err := s.assetRepository.DeleteByQueryExpr(context.Background(), queryExpr) - urns = urnsDeleted - dbErrChan <- err - close(dbErrChan) - }() +func (s *Service) deleteAssetsAsynchronously(queryExpr string) { + expr := queryexpr.SQLExpr(queryExpr) + deleteExpr := DeleteAssetExpr{ + ExprStr: &expr, + } - // Perform Elasticsearch and Lineage deletion asynchronously if the Assets deletion is successful - wg.Add(1) go func() { - defer wg.Done() - dbErr := <-dbErrChan - if dbErr == nil { - go s.deleteAssetsInElasticsearchByQueryExpr(queryExpr) - go s.deleteAssetsInLineageByQueryExpr(urns) + urnsDeleted, err := s.assetRepository.DeleteByQueryExpr(context.Background(), deleteExpr) + + // Perform Elasticsearch and Lineage deletion asynchronously ONLY IF the Assets deletion is successful + if err == nil { + go s.deleteAssetsInLineageByQueryExpr(urnsDeleted) + s.deleteAssetsInElasticsearchByQueryExpr(queryExpr) } else { - log.Printf("Database deletion failed, skipping Elasticsearch and Lineage deletions: %s", dbErr) + // TODO: need to reconsider how to properly handle error + log.Printf("Database deletion failed, skipping Elasticsearch and Lineage deletions. Err: %v", err) } }() } diff --git a/core/asset/service_test.go b/core/asset/service_test.go index b608691e..a80b3a31 100644 --- a/core/asset/service_test.go +++ b/core/asset/service_test.go @@ -446,13 +446,13 @@ func TestService_DeleteAsset(t *testing.T) { } func TestService_DeleteAssets(t *testing.T) { - emptyQueryExpr := "" - notMeetIdentifierRequirementQueryExpr := `refreshed_at < now()` - successfulQueryExpr := `refreshed_at <= "2023-12-12 23:59:59" && service in ["service-1", "service-2"] && type == "table"` + dummyRequest := asset.DeleteAssetsRequest{ + QueryExpr: `testing < now()`, + DryRun: false, + } type testCase struct { Description string - QueryExpr string - DryRun bool + Request asset.DeleteAssetsRequest Setup func(context.Context, *mocks.AssetRepository, *mocks.DiscoveryRepository, *mocks.LineageRepository) ExpectAffectedRows uint32 ExpectErr error @@ -460,32 +460,20 @@ func TestService_DeleteAssets(t *testing.T) { testCases := []testCase{ { - Description: `should return error if query expr is empty`, - QueryExpr: emptyQueryExpr, - DryRun: false, - Setup: func(ctx context.Context, ar *mocks.AssetRepository, _ *mocks.DiscoveryRepository, _ *mocks.LineageRepository) { - ar.EXPECT().GetCountByQueryExpr(ctx, emptyQueryExpr, true).Return(0, errors.New("error")) - }, - ExpectAffectedRows: 0, - ExpectErr: errors.New("error"), - }, - { - Description: `should return error if query expr does not meet identifier requirement`, - QueryExpr: notMeetIdentifierRequirementQueryExpr, - DryRun: false, + Description: `should return error if query expr not valid`, + Request: dummyRequest, Setup: func(ctx context.Context, ar *mocks.AssetRepository, _ *mocks.DiscoveryRepository, _ *mocks.LineageRepository) { - ar.EXPECT().GetCountByQueryExpr(ctx, notMeetIdentifierRequirementQueryExpr, true). - Return(0, errors.New("must exist these identifiers: refreshed_at, type, and service. Current identifiers: refreshed_at")) + ar.EXPECT().GetCountByQueryExpr(ctx, mock.AnythingOfType("*asset.DeleteAssetExpr")). + Return(0, errors.New("something wrong")) }, ExpectAffectedRows: 0, - ExpectErr: errors.New("must exist these identifiers: refreshed_at, type, and service. Current identifiers: refreshed_at"), + ExpectErr: errors.New("something wrong"), }, { - Description: `should only return the numbers of assets that match the given query if dry run is true`, - QueryExpr: successfulQueryExpr, - DryRun: true, + Description: `should only return the numbers of assets that match the given query`, + Request: dummyRequest, Setup: func(ctx context.Context, ar *mocks.AssetRepository, _ *mocks.DiscoveryRepository, _ *mocks.LineageRepository) { - ar.EXPECT().GetCountByQueryExpr(ctx, successfulQueryExpr, true).Return(11, nil) + ar.EXPECT().GetCountByQueryExpr(ctx, mock.AnythingOfType("*asset.DeleteAssetExpr")).Return(11, nil) }, ExpectAffectedRows: 11, ExpectErr: nil, @@ -509,7 +497,7 @@ func TestService_DeleteAssets(t *testing.T) { Worker: workermanager.NewInSituWorker(workermanager.Deps{DiscoveryRepo: discoveryRepo}), }) - affectedRows, err := svc.DeleteAssets(ctx, tc.QueryExpr, tc.DryRun) + affectedRows, err := svc.DeleteAssets(ctx, tc.Request) if tc.ExpectErr != nil { assert.ErrorContains(t, err, tc.ExpectErr.Error()) diff --git a/internal/server/v1beta1/asset.go b/internal/server/v1beta1/asset.go index 2abf5a31..a4dfa4b7 100644 --- a/internal/server/v1beta1/asset.go +++ b/internal/server/v1beta1/asset.go @@ -30,7 +30,7 @@ type AssetService interface { UpsertAsset(ctx context.Context, ast *asset.Asset, upstreams, downstreams []string) (string, error) UpsertAssetWithoutLineage(ctx context.Context, ast *asset.Asset) (string, error) DeleteAsset(ctx context.Context, id string) error - DeleteAssets(ctx context.Context, queryExpr string, dryRun bool) (uint32, error) + DeleteAssets(ctx context.Context, request asset.DeleteAssetsRequest) (uint32, error) GetLineage(ctx context.Context, urn string, query asset.LineageQuery) (asset.Lineage, error) GetTypes(ctx context.Context, flt asset.Filter) (map[asset.Type]int, error) @@ -309,16 +309,18 @@ func (server *APIServer) DeleteAsset(ctx context.Context, req *compassv1beta1.De func (server *APIServer) DeleteAssets(ctx context.Context, req *compassv1beta1.DeleteAssetsRequest) (*compassv1beta1.DeleteAssetsResponse, error) { var affectedRows uint32 - _, err := server.ValidateUserInCtx(ctx) - if err != nil { + if _, err := server.ValidateUserInCtx(ctx); err != nil { return nil, err } defer func() { - server.logger.Warn("the number of affected rows is %d", affectedRows) + server.logger.Warn(fmt.Sprintf("the number of affected rows is %d. delete request: %v", affectedRows, req)) }() - server.logger.Warn("delete request: %v", req) - affectedRows, err = server.assetService.DeleteAssets(ctx, req.QueryExpr, req.DryRun) + deleteAssetsRequest := asset.DeleteAssetsRequest{ + QueryExpr: req.QueryExpr, + DryRun: req.DryRun, + } + affectedRows, err := server.assetService.DeleteAssets(ctx, deleteAssetsRequest) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } diff --git a/internal/server/v1beta1/asset_test.go b/internal/server/v1beta1/asset_test.go index 4d5c353d..221c663d 100644 --- a/internal/server/v1beta1/asset_test.go +++ b/internal/server/v1beta1/asset_test.go @@ -985,11 +985,13 @@ func TestDeleteAsset(t *testing.T) { func TestDeleteAssets(t *testing.T) { var ( - userID = uuid.NewString() - userUUID = uuid.NewString() - emptyQueryExpr = "" - notMeetIdentifierRequirementQueryExpr = `refreshed_at < now()` - successfulQueryExpr = `refreshed_at <= "2023-12-12 23:59:59" && service in ["service-1", "service-2"] && type == "table"` + userID = uuid.NewString() + userUUID = uuid.NewString() + dummyQuery = "testing < now()" + dummyRequest = asset.DeleteAssetsRequest{ + QueryExpr: dummyQuery, + DryRun: false, + } ) type TestCase struct { Description string @@ -1003,43 +1005,33 @@ func TestDeleteAssets(t *testing.T) { testCases := []TestCase{ { Description: "should return error when insert empty query expr", - QueryExpr: emptyQueryExpr, + QueryExpr: dummyQuery, DryRun: false, ExpectStatus: codes.InvalidArgument, ExpectResult: nil, Setup: func(ctx context.Context, as *mocks.AssetService, astID string) { - as.EXPECT().DeleteAssets(ctx, emptyQueryExpr, false).Return(0, errors.New("error")) + as.EXPECT().DeleteAssets(ctx, dummyRequest).Return(0, errors.New("error")) }, }, { Description: "should return error when query expr does not meet identifier requirement", - QueryExpr: notMeetIdentifierRequirementQueryExpr, + QueryExpr: dummyQuery, DryRun: false, ExpectStatus: codes.InvalidArgument, ExpectResult: nil, Setup: func(ctx context.Context, as *mocks.AssetService, astID string) { - as.EXPECT().DeleteAssets(ctx, notMeetIdentifierRequirementQueryExpr, false). + as.EXPECT().DeleteAssets(ctx, dummyRequest). Return(0, errors.New("must exist these identifiers: refreshed_at, type, and service. Current identifiers: refreshed_at")) }, }, { - Description: `should only return the numbers of assets that match the given query if dry run is true`, - QueryExpr: successfulQueryExpr, - DryRun: true, - ExpectStatus: codes.OK, - ExpectResult: &compassv1beta1.DeleteAssetsResponse{AffectedRows: 11}, - Setup: func(ctx context.Context, as *mocks.AssetService, astID string) { - as.EXPECT().DeleteAssets(ctx, successfulQueryExpr, true).Return(11, nil) - }, - }, - { - Description: `should return the affected rows numbers and perform deletion in the background if dry run is false`, - QueryExpr: successfulQueryExpr, + Description: `should only return the numbers of assets that match the given query`, + QueryExpr: dummyQuery, DryRun: false, ExpectStatus: codes.OK, - ExpectResult: &compassv1beta1.DeleteAssetsResponse{AffectedRows: 2}, + ExpectResult: &compassv1beta1.DeleteAssetsResponse{AffectedRows: 11}, Setup: func(ctx context.Context, as *mocks.AssetService, astID string) { - as.EXPECT().DeleteAssets(ctx, successfulQueryExpr, false).Return(2, nil) + as.EXPECT().DeleteAssets(ctx, dummyRequest).Return(11, nil) }, }, } diff --git a/internal/server/v1beta1/mocks/asset_service.go b/internal/server/v1beta1/mocks/asset_service.go index 87121e2d..f5eb651e 100644 --- a/internal/server/v1beta1/mocks/asset_service.go +++ b/internal/server/v1beta1/mocks/asset_service.go @@ -110,23 +110,23 @@ func (_c *AssetService_DeleteAsset_Call) RunAndReturn(run func(context.Context, return _c } -// DeleteAssets provides a mock function with given fields: ctx, queryExpr, dryRun -func (_m *AssetService) DeleteAssets(ctx context.Context, queryExpr string, dryRun bool) (uint32, error) { - ret := _m.Called(ctx, queryExpr, dryRun) +// DeleteAssets provides a mock function with given fields: ctx, request +func (_m *AssetService) DeleteAssets(ctx context.Context, request asset.DeleteAssetsRequest) (uint32, error) { + ret := _m.Called(ctx, request) var r0 uint32 var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, bool) (uint32, error)); ok { - return rf(ctx, queryExpr, dryRun) + if rf, ok := ret.Get(0).(func(context.Context, asset.DeleteAssetsRequest) (uint32, error)); ok { + return rf(ctx, request) } - if rf, ok := ret.Get(0).(func(context.Context, string, bool) uint32); ok { - r0 = rf(ctx, queryExpr, dryRun) + if rf, ok := ret.Get(0).(func(context.Context, asset.DeleteAssetsRequest) uint32); ok { + r0 = rf(ctx, request) } else { r0 = ret.Get(0).(uint32) } - if rf, ok := ret.Get(1).(func(context.Context, string, bool) error); ok { - r1 = rf(ctx, queryExpr, dryRun) + if rf, ok := ret.Get(1).(func(context.Context, asset.DeleteAssetsRequest) error); ok { + r1 = rf(ctx, request) } else { r1 = ret.Error(1) } @@ -141,15 +141,14 @@ type AssetService_DeleteAssets_Call struct { // DeleteAssets is a helper method to define mock.On call // - ctx context.Context -// - queryExpr string -// - dryRun bool -func (_e *AssetService_Expecter) DeleteAssets(ctx interface{}, queryExpr interface{}, dryRun interface{}) *AssetService_DeleteAssets_Call { - return &AssetService_DeleteAssets_Call{Call: _e.mock.On("DeleteAssets", ctx, queryExpr, dryRun)} +// - request asset.DeleteAssetsRequest +func (_e *AssetService_Expecter) DeleteAssets(ctx interface{}, request interface{}) *AssetService_DeleteAssets_Call { + return &AssetService_DeleteAssets_Call{Call: _e.mock.On("DeleteAssets", ctx, request)} } -func (_c *AssetService_DeleteAssets_Call) Run(run func(ctx context.Context, queryExpr string, dryRun bool)) *AssetService_DeleteAssets_Call { +func (_c *AssetService_DeleteAssets_Call) Run(run func(ctx context.Context, request asset.DeleteAssetsRequest)) *AssetService_DeleteAssets_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(bool)) + run(args[0].(context.Context), args[1].(asset.DeleteAssetsRequest)) }) return _c } @@ -159,7 +158,7 @@ func (_c *AssetService_DeleteAssets_Call) Return(_a0 uint32, _a1 error) *AssetSe return _c } -func (_c *AssetService_DeleteAssets_Call) RunAndReturn(run func(context.Context, string, bool) (uint32, error)) *AssetService_DeleteAssets_Call { +func (_c *AssetService_DeleteAssets_Call) RunAndReturn(run func(context.Context, asset.DeleteAssetsRequest) (uint32, error)) *AssetService_DeleteAssets_Call { _c.Call.Return(run) return _c } diff --git a/internal/store/elasticsearch/discovery_repository.go b/internal/store/elasticsearch/discovery_repository.go index 0cc0362f..0c6c4e11 100644 --- a/internal/store/elasticsearch/discovery_repository.go +++ b/internal/store/elasticsearch/discovery_repository.go @@ -12,7 +12,7 @@ import ( "time" "github.com/goto/compass/core/asset" - queryexpr "github.com/goto/compass/pkg/query_expr" + queryexpr "github.com/goto/compass/pkg/queryexpr" "github.com/goto/salt/log" ) @@ -145,13 +145,14 @@ func (repo *DiscoveryRepository) DeleteByURN(ctx context.Context, assetURN strin return repo.deleteWithQuery(ctx, "DeleteByURN", fmt.Sprintf(`{"query":{"term":{"urn.keyword": %q}}}`, assetURN)) } +// TODO: Integration testing func (repo *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) error { - if queryExpr == "" { + if strings.TrimSpace(queryExpr) == "" { return asset.ErrEmptyQuery } expr := queryexpr.ESExpr(queryExpr) - deleteAssetESExpr := &queryexpr.DeleteAssetExpr{ + deleteAssetESExpr := &asset.DeleteAssetExpr{ ExprStr: &expr, } esQuery, err := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) diff --git a/internal/store/elasticsearch/discovery_repository_test.go b/internal/store/elasticsearch/discovery_repository_test.go index dacd0101..77c57af3 100644 --- a/internal/store/elasticsearch/discovery_repository_test.go +++ b/internal/store/elasticsearch/discovery_repository_test.go @@ -9,7 +9,7 @@ import ( "github.com/goto/compass/core/asset" store "github.com/goto/compass/internal/store/elasticsearch" - queryexpr "github.com/goto/compass/pkg/query_expr" + "github.com/goto/compass/pkg/queryexpr" "github.com/goto/salt/log" "github.com/olivere/elastic/v7" "github.com/stretchr/testify/assert" @@ -433,7 +433,7 @@ func TestDiscoveryRepositoryDeleteByQueryExpr(t *testing.T) { assert.NoError(t, err) expr := queryexpr.ESExpr(queryExpr) - deleteAssetESExpr := &queryexpr.DeleteAssetExpr{ + deleteAssetESExpr := &asset.DeleteAssetExpr{ ExprStr: &expr, } esQuery, _ := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) diff --git a/internal/store/postgres/asset_model.go b/internal/store/postgres/asset_model.go index 5637f2b0..59da36df 100644 --- a/internal/store/postgres/asset_model.go +++ b/internal/store/postgres/asset_model.go @@ -14,20 +14,20 @@ import ( ) type AssetModel struct { - ID string `db:"id"` - URN string `db:"urn"` - Type string `db:"type"` - Name string `db:"name"` - Service string `db:"service"` - Description string `db:"description"` - Data JSONMap `db:"data"` - URL string `db:"url"` - Labels JSONMap `db:"labels"` - Version string `db:"version"` - UpdatedBy UserModel `db:"updated_by"` - CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"updated_at"` - RefreshedAt time.Time `db:"refreshed_at"` + ID string `db:"id"` + URN string `db:"urn"` + Type string `db:"type"` + Name string `db:"name"` + Service string `db:"service"` + Description string `db:"description"` + Data JSONMap `db:"data"` + URL string `db:"url"` + Labels JSONMap `db:"labels"` + Version string `db:"version"` + UpdatedBy UserModel `db:"updated_by"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` + RefreshedAt *time.Time `db:"refreshed_at"` // version specific information Changelog types.JSONText `db:"changelog"` Owners types.JSONText `db:"owners"` diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index 816f85c5..85edb5ac 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -13,7 +13,7 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/goto/compass/core/asset" "github.com/goto/compass/core/user" - queryexpr "github.com/goto/compass/pkg/query_expr" + "github.com/goto/compass/pkg/queryexpr" "github.com/jmoiron/sqlx" "github.com/r3labs/diff/v2" ) @@ -117,28 +117,13 @@ func (r *AssetRepository) GetCount(ctx context.Context, flt asset.Filter) (int, } // GetCountByQueryExpr retrieves number of assets for every type based on query expr -func (r *AssetRepository) GetCountByQueryExpr(ctx context.Context, queryExpr string, isDeleteExpr bool) (int, error) { - var sqlQuery string - if isDeleteExpr { - expr := queryexpr.SQLExpr(queryExpr) - deleteExpr := &queryexpr.DeleteAssetExpr{ - ExprStr: &expr, - } - query, err := queryexpr.ValidateAndGetQueryFromExpr(deleteExpr) - if err != nil { - return 0, err - } - sqlQuery = query - } else { - sqlExpr := queryexpr.SQLExpr(queryExpr) - query, err := queryexpr.ValidateAndGetQueryFromExpr(&sqlExpr) - if err != nil { - return 0, err - } - sqlQuery = query +func (r *AssetRepository) GetCountByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) (int, error) { + query, err := queryexpr.ValidateAndGetQueryFromExpr(queryExpr) + if err != nil { + return 0, err } - total, err := r.getCountByQuery(ctx, sqlQuery) + total, err := r.getCountByQuery(ctx, query) if err != nil { return 0, err } @@ -400,39 +385,18 @@ func (r *AssetRepository) DeleteByURN(ctx context.Context, urn string) error { return nil } -func (r *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) ([]string, error) { - var allURNs []string +func (r *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) ([]string, error) { + var urns []string err := r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { - expr := queryexpr.SQLExpr(queryExpr) - deleteExpr := &queryexpr.DeleteAssetExpr{ - ExprStr: &expr, - } - query, err := queryexpr.ValidateAndGetQueryFromExpr(deleteExpr) + query, err := queryexpr.ValidateAndGetQueryFromExpr(queryExpr) if err != nil { return err } - var lastID string - for { - // Fetch a batch of rows to delete using the last ID as a marker - urns, nextLastID, err := r.getAllURNsWithBatch(ctx, query, lastID) - if err != nil { - log.Printf("Failed to get batch to delete: %v", err) - return err - } - - // If no more rows to delete, exit the loop - if len(urns) == 0 { - break - } - - if err := r.deleteByURNs(ctx, urns); err != nil { - log.Printf("Failed to delete rows: %v", err) - return err - } - - allURNs = append(allURNs, urns...) - lastID = nextLastID + urns, err = r.deleteByQueryAndReturnURNS(ctx, query) + if err != nil { + log.Printf("Failed to delete by query expr: %v", err) + return err } return nil @@ -442,59 +406,26 @@ func (r *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr strin return nil, err } - return allURNs, nil + return urns, nil } -func (r *AssetRepository) deleteByURNs(ctx context.Context, urns []string) error { - query, args, err := sq.Delete("assets"). - Where(sq.Eq{"urn": urns}). - PlaceholderFormat(sq.Dollar). - ToSql() - if err != nil { - return fmt.Errorf("error building query: %w", err) - } - - _, err = r.client.db.ExecContext(ctx, query, args...) - if err != nil { - return err - } - - return nil -} - -// getAllURNsWithBatch retrieves list of asset URNs as filters without consider user -func (r *AssetRepository) getAllURNsWithBatch(ctx context.Context, whereCondition string, lastID string) ([]string, string, error) { - var rows []struct { - ID string `db:"id"` - URN string `db:"urn"` - } - - builder := sq.Select("id", "urn"). - From("assets"). +// deleteByQueryAndReturnURNS remove all assets that match to query and return array of urn of asset that deleted. +func (r *AssetRepository) deleteByQueryAndReturnURNS(ctx context.Context, whereCondition string) ([]string, error) { + builder := sq.Delete("assets"). Where(whereCondition). - OrderBy("id ASC"). - Limit(batchSize) + Suffix("RETURNING urn") - if lastID != "" { - builder = builder.Where(sq.Gt{"id": lastID}) - } query, args, err := builder.PlaceholderFormat(sq.Dollar).ToSql() if err != nil { - return nil, "", fmt.Errorf("error building query: %w", err) - } - - if err := r.client.db.SelectContext(ctx, &rows, query, args...); err != nil { - return nil, "", err + return nil, fmt.Errorf("error building query: %w", err) } var urns []string - var nextLastID string - for _, row := range rows { - urns = append(urns, row.URN) - nextLastID = row.ID + if err := r.client.db.SelectContext(ctx, &urns, query, args...); err != nil { + return nil, err } - return urns, nextLastID, nil + return urns, nil } func (r *AssetRepository) AddProbe(ctx context.Context, assetURN string, probe *asset.Probe) error { @@ -628,11 +559,17 @@ func (r *AssetRepository) deleteWithPredicate(ctx context.Context, pred sq.Eq) ( func (r *AssetRepository) insert(ctx context.Context, ast *asset.Asset) (string, error) { var id string err := r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { - ast.CreatedAt = ast.RefreshedAt // current time - ast.UpdatedAt = ast.CreatedAt + currentTime := time.Now() + if ast.RefreshedAt != nil { + currentTime = *ast.RefreshedAt + } + ast.CreatedAt = currentTime + ast.UpdatedAt = currentTime query, args, err := sq.Insert("assets"). - Columns("urn", "type", "service", "name", "description", "data", "url", "labels", "created_at", "updated_by", "updated_at", "refreshed_at", "version"). - Values(ast.URN, ast.Type, ast.Service, ast.Name, ast.Description, ast.Data, ast.URL, ast.Labels, ast.CreatedAt, ast.UpdatedBy.ID, ast.UpdatedAt, ast.RefreshedAt, asset.BaseVersion). + Columns("urn", "type", "service", "name", "description", "data", "url", "labels", + "created_at", "updated_by", "updated_at", "refreshed_at", "version"). + Values(ast.URN, ast.Type, ast.Service, ast.Name, ast.Description, ast.Data, ast.URL, ast.Labels, + ast.CreatedAt, ast.UpdatedBy.ID, ast.UpdatedAt, currentTime, asset.BaseVersion). Suffix("RETURNING \"id\""). PlaceholderFormat(sq.Dollar). ToSql() @@ -678,14 +615,13 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, } if len(clog) == 0 { - return nil - } + if newAsset.RefreshedAt != oldAsset.RefreshedAt { + return r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { + return r.updateAsset(ctx, tx, assetID, newAsset) + }) + } - refreshedOnly := len(clog) == 1 && clog[0].Path[0] == "RefreshedAt" - if refreshedOnly { - return r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { - return r.updateAsset(ctx, tx, assetID, newAsset) - }) + return nil } return r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { @@ -696,7 +632,7 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, } newAsset.Version = newVersion newAsset.ID = oldAsset.ID - newAsset.UpdatedAt = newAsset.RefreshedAt // current time + newAsset.UpdatedAt = *newAsset.RefreshedAt // current time if err := r.updateAsset(ctx, tx, assetID, newAsset); err != nil { return err @@ -725,6 +661,10 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, } func (r *AssetRepository) updateAsset(ctx context.Context, tx *sqlx.Tx, assetID string, newAsset *asset.Asset) error { + currentTime := time.Now() + if newAsset.RefreshedAt != nil { + currentTime = *newAsset.RefreshedAt + } query, args, err := sq.Update("assets"). Set("urn", newAsset.URN). Set("type", newAsset.Type). @@ -735,7 +675,7 @@ func (r *AssetRepository) updateAsset(ctx context.Context, tx *sqlx.Tx, assetID Set("url", newAsset.URL). Set("labels", newAsset.Labels). Set("updated_at", newAsset.UpdatedAt). - Set("refreshed_at", newAsset.RefreshedAt). + Set("refreshed_at", currentTime). Set("updated_by", newAsset.UpdatedBy.ID). Set("version", newAsset.Version). Where(sq.Eq{"id": assetID}). diff --git a/internal/store/postgres/asset_repository_test.go b/internal/store/postgres/asset_repository_test.go index 07da42a5..075fe1ad 100644 --- a/internal/store/postgres/asset_repository_test.go +++ b/internal/store/postgres/asset_repository_test.go @@ -18,6 +18,7 @@ import ( "github.com/goto/compass/core/user" "github.com/goto/compass/internal/store/postgres" "github.com/goto/compass/internal/testutils" + "github.com/goto/compass/pkg/queryexpr" "github.com/goto/salt/log" "github.com/r3labs/diff/v2" "github.com/stretchr/testify/suite" @@ -976,13 +977,14 @@ func (r *AssetRepositoryTestSuite) TestVersions() { func (r *AssetRepositoryTestSuite) TestUpsert() { r.Run("on insert", func() { r.Run("set ID to asset and version to base version", func() { + currentTime := time.Now() ast := asset.Asset{ URN: "urn-u-1", Type: "table", Service: "bigquery", URL: "https://sample-url.com", UpdatedBy: r.users[0], - RefreshedAt: time.Now(), + RefreshedAt: ¤tTime, } id, err := r.repository.Upsert(r.ctx, &ast) r.Equal(asset.BaseVersion, ast.Version) @@ -999,7 +1001,7 @@ func (r *AssetRepositoryTestSuite) TestUpsert() { r.assertAsset(&ast, &assetInDB) ast2 := ast - ast2.RefreshedAt = time.Now() + ast2.RefreshedAt = ¤tTime ast2.Description = "create a new version" // to force fetch from asset_versions. _, err = r.repository.Upsert(r.ctx, &ast2) r.NoError(err) @@ -1319,12 +1321,13 @@ func (r *AssetRepositoryTestSuite) TestDeleteByURN() { func (r *AssetRepositoryTestSuite) TestDeleteByQueryExpr() { r.Run("should delete correct asset", func() { currentTime := time.Now() + oneYearAgoTime := currentTime.AddDate(-1, 0, 0) asset1 := asset.Asset{ URN: "urn-del-1", Type: "table", Service: "bigquery", UpdatedBy: user.User{ID: defaultAssetUpdaterUserID}, - RefreshedAt: currentTime.AddDate(-1, 0, 0), + RefreshedAt: &oneYearAgoTime, } asset2 := asset.Asset{ URN: "urn-del-2", @@ -1332,7 +1335,7 @@ func (r *AssetRepositoryTestSuite) TestDeleteByQueryExpr() { Service: "kafka", Version: asset.BaseVersion, UpdatedBy: user.User{ID: defaultAssetUpdaterUserID}, - RefreshedAt: currentTime.AddDate(-1, 0, 0), + RefreshedAt: &oneYearAgoTime, } _, err := r.repository.Upsert(r.ctx, &asset1) @@ -1341,10 +1344,14 @@ func (r *AssetRepositoryTestSuite) TestDeleteByQueryExpr() { id, err := r.repository.Upsert(r.ctx, &asset2) r.Require().NoError(err) - queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + query := "refreshed_at <= '" + currentTime.Format("2006-01-02 15:04:05") + "' && service == '" + asset1.Service + "' && type == '" + asset1.Type.String() + "' && urn == '" + asset1.URN + "'" + sqlExpr := queryexpr.SQLExpr(query) + queryExpr := &asset.DeleteAssetExpr{ + ExprStr: &sqlExpr, + } urns, err := r.repository.DeleteByQueryExpr(r.ctx, queryExpr) r.NoError(err) r.Equal([]string{"urn-del-1"}, urns) @@ -1963,13 +1970,13 @@ func (r *AssetRepositoryTestSuite) assertAsset(expectedAsset, actualAsset *asset // sanitize time to make the assets comparable expectedAsset.CreatedAt = time.Time{} expectedAsset.UpdatedAt = time.Time{} - expectedAsset.RefreshedAt = time.Time{} + expectedAsset.RefreshedAt = &time.Time{} expectedAsset.UpdatedBy.CreatedAt = time.Time{} expectedAsset.UpdatedBy.UpdatedAt = time.Time{} actualAsset.CreatedAt = time.Time{} actualAsset.UpdatedAt = time.Time{} - actualAsset.RefreshedAt = time.Time{} + actualAsset.RefreshedAt = &time.Time{} actualAsset.UpdatedBy.CreatedAt = time.Time{} actualAsset.UpdatedBy.UpdatedAt = time.Time{} diff --git a/internal/workermanager/discovery_worker.go b/internal/workermanager/discovery_worker.go index 7fac7fe5..10b1875e 100644 --- a/internal/workermanager/discovery_worker.go +++ b/internal/workermanager/discovery_worker.go @@ -4,10 +4,9 @@ import ( "context" "encoding/json" "fmt" - "strings" - "github.com/goto/compass/core/asset" "github.com/goto/compass/pkg/worker" + "strings" ) //go:generate mockery --name=DiscoveryRepository -r --case underscore --with-expecter --structname DiscoveryRepository --filename discovery_repository_mock.go --output=./mocks @@ -26,7 +25,7 @@ func (m *Manager) EnqueueIndexAssetJob(ctx context.Context, ast asset.Asset) err } err = m.worker.Enqueue(ctx, worker.JobSpec{ - Type: JobIndexAsset, + Type: jobIndexAsset, Payload: payload, }) if err != nil { @@ -128,7 +127,7 @@ func (m *Manager) SyncAssets(ctx context.Context, job worker.JobSpec) error { func (m *Manager) EnqueueDeleteAssetJob(ctx context.Context, urn string) error { err := m.worker.Enqueue(ctx, worker.JobSpec{ - Type: JobDeleteAsset, + Type: jobDeleteAsset, Payload: ([]byte)(urn), }) if err != nil { @@ -161,7 +160,7 @@ func (m *Manager) DeleteAsset(ctx context.Context, job worker.JobSpec) error { func (m *Manager) EnqueueDeleteAssetsByQueryExprJob(ctx context.Context, queryExpr string) error { err := m.worker.Enqueue(ctx, worker.JobSpec{ - Type: JobDeleteAssetsByQuery, + Type: jobDeleteAssetsByQuery, Payload: ([]byte)(queryExpr), }) if err != nil { @@ -194,7 +193,7 @@ func (m *Manager) DeleteAssetsByQueryExpr(ctx context.Context, job worker.JobSpe func (m *Manager) EnqueueSyncAssetJob(ctx context.Context, service string) error { err := m.worker.Enqueue(ctx, worker.JobSpec{ - Type: JobSyncAsset, + Type: jobSyncAsset, Payload: ([]byte)(service), }) if err != nil { diff --git a/internal/workermanager/discovery_worker_test.go b/internal/workermanager/discovery_worker_test.go index c30793fc..8cd0f1ca 100644 --- a/internal/workermanager/discovery_worker_test.go +++ b/internal/workermanager/discovery_worker_test.go @@ -33,7 +33,7 @@ func TestManager_EnqueueIndexAssetJob(t *testing.T) { wrkr := mocks.NewWorker(t) wrkr.EXPECT(). Enqueue(ctx, worker.JobSpec{ - Type: workermanager.JobIndexAsset, + Type: "index-asset", Payload: testutils.Marshal(t, sampleAsset), }). Return(tc.enqueueErr) @@ -75,7 +75,7 @@ func TestManager_IndexAsset(t *testing.T) { DiscoveryRepo: discoveryRepo, }) err := mgr.IndexAsset(ctx, worker.JobSpec{ - Type: workermanager.JobIndexAsset, + Type: "index-asset", Payload: testutils.Marshal(t, sampleAsset), }) if tc.expectedErr { @@ -107,7 +107,7 @@ func TestManager_EnqueueDeleteAssetJob(t *testing.T) { wrkr := mocks.NewWorker(t) wrkr.EXPECT(). Enqueue(ctx, worker.JobSpec{ - Type: workermanager.JobDeleteAsset, + Type: "delete-asset", Payload: []byte("some-urn"), }). Return(tc.enqueueErr) @@ -147,7 +147,7 @@ func TestManager_DeleteAsset(t *testing.T) { DiscoveryRepo: discoveryRepo, }) err := mgr.DeleteAsset(ctx, worker.JobSpec{ - Type: workermanager.JobDeleteAsset, + Type: "delete-asset", Payload: []byte("some-urn"), }) if tc.expectedErr { @@ -183,7 +183,7 @@ func TestManager_EnqueueDeleteAssetsByQueryExprJob(t *testing.T) { wrkr := mocks.NewWorker(t) wrkr.EXPECT(). Enqueue(ctx, worker.JobSpec{ - Type: workermanager.JobDeleteAssetsByQuery, + Type: "delete-assets-by-query", Payload: []byte(queryExpr), }). Return(tc.enqueueErr) @@ -227,7 +227,7 @@ func TestManager_DeleteAssets(t *testing.T) { DiscoveryRepo: discoveryRepo, }) err := mgr.DeleteAssetsByQueryExpr(ctx, worker.JobSpec{ - Type: workermanager.JobDeleteAssetsByQuery, + Type: "delete-assets-by-query", Payload: []byte(queryExpr), }) if tc.expectedErr { diff --git a/internal/workermanager/job_types.go b/internal/workermanager/job_types.go index 3e34ddf1..7b2849b6 100644 --- a/internal/workermanager/job_types.go +++ b/internal/workermanager/job_types.go @@ -1,8 +1,8 @@ package workermanager const ( - JobIndexAsset = "index-asset" - JobDeleteAsset = "delete-asset" - JobDeleteAssetsByQuery = "delete-assets-by-query" - JobSyncAsset = "sync-asset" + jobIndexAsset = "index-asset" + jobDeleteAsset = "delete-asset" + jobDeleteAssetsByQuery = "delete-assets-by-query" + jobSyncAsset = "sync-asset" ) diff --git a/internal/workermanager/worker_manager.go b/internal/workermanager/worker_manager.go index 91559ddd..ed795e52 100644 --- a/internal/workermanager/worker_manager.go +++ b/internal/workermanager/worker_manager.go @@ -124,10 +124,10 @@ func (m *Manager) init() error { m.initDone.Store(true) jobHandlers := map[string]worker.JobHandler{ - JobIndexAsset: m.indexAssetHandler(), - JobDeleteAsset: m.deleteAssetHandler(), - JobDeleteAssetsByQuery: m.deleteAssetsByQueryHandler(), - JobSyncAsset: m.syncAssetHandler(), + jobIndexAsset: m.indexAssetHandler(), + jobDeleteAsset: m.deleteAssetHandler(), + jobDeleteAssetsByQuery: m.deleteAssetsByQueryHandler(), + jobSyncAsset: m.syncAssetHandler(), } for typ, h := range jobHandlers { if err := m.worker.Register(typ, h); err != nil { diff --git a/internal/workermanager/worker_manager_test.go b/internal/workermanager/worker_manager_test.go index 41b30cae..0efc4bf1 100644 --- a/internal/workermanager/worker_manager_test.go +++ b/internal/workermanager/worker_manager_test.go @@ -77,16 +77,16 @@ func TestManager_Run(t *testing.T) { wrkr := mocks.NewWorker(t) mgr := workermanager.NewWithWorker(wrkr, workermanager.Deps{}) wrkr.EXPECT(). - Register(workermanager.JobIndexAsset, mock.AnythingOfType("worker.JobHandler")). + Register("index-asset", mock.AnythingOfType("worker.JobHandler")). Return(nil) wrkr.EXPECT(). - Register(workermanager.JobDeleteAsset, mock.AnythingOfType("worker.JobHandler")). + Register("delete-asset", mock.AnythingOfType("worker.JobHandler")). Return(nil) wrkr.EXPECT(). - Register(workermanager.JobDeleteAssetsByQuery, mock.AnythingOfType("worker.JobHandler")). + Register("delete-assets-by-query", mock.AnythingOfType("worker.JobHandler")). Return(nil) wrkr.EXPECT(). - Register(workermanager.JobSyncAsset, mock.AnythingOfType("worker.JobHandler")). + Register("sync-asset", mock.AnythingOfType("worker.JobHandler")). Return(nil) wrkr.EXPECT(). Run(ctx). diff --git a/pkg/query_expr/es_expr.go b/pkg/queryexpr/es_expr.go similarity index 92% rename from pkg/query_expr/es_expr.go rename to pkg/queryexpr/es_expr.go index 9199714d..4e6c8e9e 100644 --- a/pkg/query_expr/es_expr.go +++ b/pkg/queryexpr/es_expr.go @@ -7,15 +7,18 @@ import ( "github.com/expr-lang/expr/ast" ) +var KeywordIdentifiers = [...]string{"service"} + +// TODO: Consider not to be pointer type ESExpr string func (e *ESExpr) String() string { return string(*e) } -// ToQuery default +// ToQuery default: convert to be Elasticsearch query func (e *ESExpr) ToQuery() (string, error) { - queryExprParsed, err := GetTreeNodeFromQueryExpr(e.String()) + queryExprParsed, err := getTreeNodeFromQueryExpr(e.String()) if err != nil { return "", err } @@ -30,7 +33,6 @@ func (e *ESExpr) ToQuery() (string, error) { } esQuery = map[string]interface{}{"query": esQuery} - // Convert to JSON queryJSON, err := json.Marshal(esQuery) if err != nil { return "", err @@ -56,6 +58,9 @@ func (e *ESExpr) translateToEsQuery(node ast.Node) (interface{}, error) { case *ast.NilNode: return nil, nil case *ast.IdentifierNode: + if e.isKeywordIdentifier(n) { + return fmt.Sprintf("%s.keyword", n.Value), nil + } return n.Value, nil case *ast.IntegerNode: return n.Value, nil @@ -148,6 +153,15 @@ func (e *ESExpr) binaryNodeToEsQuery(n *ast.BinaryNode) (interface{}, error) { / } } +func (*ESExpr) isKeywordIdentifier(node *ast.IdentifierNode) bool { + for _, keyword := range KeywordIdentifiers { + if node.Value == keyword { + return true + } + } + return false +} + func (e *ESExpr) unaryNodeToEsQuery(n *ast.UnaryNode) (interface{}, error) { switch n.Operator { case "not": diff --git a/pkg/query_expr/es_expr_test.go b/pkg/queryexpr/es_expr_test.go similarity index 98% rename from pkg/query_expr/es_expr_test.go rename to pkg/queryexpr/es_expr_test.go index 272c31c6..e4e83890 100644 --- a/pkg/query_expr/es_expr_test.go +++ b/pkg/queryexpr/es_expr_test.go @@ -3,7 +3,7 @@ package queryexpr_test import ( "testing" - queryexpr "github.com/goto/compass/pkg/query_expr" + "github.com/goto/compass/pkg/queryexpr" ) func TestESExpr_String(t *testing.T) { diff --git a/pkg/query_expr/query_expr.go b/pkg/queryexpr/query_expr.go similarity index 94% rename from pkg/query_expr/query_expr.go rename to pkg/queryexpr/query_expr.go index 7680efd4..80c90454 100644 --- a/pkg/query_expr/query_expr.go +++ b/pkg/queryexpr/query_expr.go @@ -59,7 +59,7 @@ func (s *ExprVisitor) Visit(node *ast.Node) { //nolint:gocritic } func GetIdentifiersMap(queryExpr string) (map[string]string, error) { - queryExprParsed, err := GetTreeNodeFromQueryExpr(queryExpr) + queryExprParsed, err := getTreeNodeFromQueryExpr(queryExpr) if err != nil { return nil, err } @@ -68,7 +68,7 @@ func GetIdentifiersMap(queryExpr string) (map[string]string, error) { return queryExprVisitor.IdentifiersWithOperator, nil } -func GetTreeNodeFromQueryExpr(queryExpr string) (ast.Node, error) { +func getTreeNodeFromQueryExpr(queryExpr string) (ast.Node, error) { parsed, err := parser.Parse(queryExpr) if err != nil { return nil, fmt.Errorf("error parsing expression: %w", err) diff --git a/pkg/query_expr/query_expr_test.go b/pkg/queryexpr/query_expr_test.go similarity index 68% rename from pkg/query_expr/query_expr_test.go rename to pkg/queryexpr/query_expr_test.go index f7d21599..02c36cfc 100644 --- a/pkg/query_expr/query_expr_test.go +++ b/pkg/queryexpr/query_expr_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - queryexpr "github.com/goto/compass/pkg/query_expr" + "github.com/goto/compass/pkg/queryexpr" ) func TestGetIdentifiersMap(t *testing.T) { @@ -98,45 +98,3 @@ func TestGetQueryExprResult(t *testing.T) { }) } } - -func TestGetTreeNodeFromQueryExpr(t *testing.T) { - tests := []struct { - name string - expr string - want any - wantErr bool - }{ - { - name: "success using func from the library", - expr: `findLast([1], # >= 1)`, - want: "findLast([1], # >= 1)", - wantErr: false, - }, - { - name: "success using equation", - expr: `false == !true`, - want: "false == !true", - wantErr: false, - }, - { - name: "got error using wrong syntax", - expr: `findLast(`, - want: nil, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := queryexpr.GetTreeNodeFromQueryExpr(tt.expr) - if (err != nil) != tt.wantErr { - t.Errorf("GetTreeNodeFromQueryExpr() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !tt.wantErr { - if got.String() != tt.want { - t.Errorf("GetTreeNodeFromQueryExpr() got = %v, want %v", got, tt.want) - } - } - }) - } -} diff --git a/pkg/query_expr/sql_expr.go b/pkg/queryexpr/sql_expr.go similarity index 98% rename from pkg/query_expr/sql_expr.go rename to pkg/queryexpr/sql_expr.go index 6fecc196..6f64e2d8 100644 --- a/pkg/query_expr/sql_expr.go +++ b/pkg/queryexpr/sql_expr.go @@ -16,7 +16,7 @@ func (s *SQLExpr) String() string { // ToQuery default func (s *SQLExpr) ToQuery() (string, error) { - queryExprParsed, err := GetTreeNodeFromQueryExpr(s.String()) + queryExprParsed, err := getTreeNodeFromQueryExpr(s.String()) if err != nil { return "", err } diff --git a/pkg/query_expr/sql_expr_test.go b/pkg/queryexpr/sql_expr_test.go similarity index 98% rename from pkg/query_expr/sql_expr_test.go rename to pkg/queryexpr/sql_expr_test.go index 16728676..3c2a9ac0 100644 --- a/pkg/query_expr/sql_expr_test.go +++ b/pkg/queryexpr/sql_expr_test.go @@ -3,7 +3,7 @@ package queryexpr_test import ( "testing" - queryexpr "github.com/goto/compass/pkg/query_expr" + "github.com/goto/compass/pkg/queryexpr" ) func TestSQLExpr_String(t *testing.T) { From 6dc3c6ce7ba650f560b960dff84006c95afbdaa8 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Tue, 20 Aug 2024 15:06:44 +0700 Subject: [PATCH 32/45] test: fix unit test --- core/asset/delete_asset_expr_test.go | 2 +- core/asset/service_test.go | 3 +- .../discovery_repository_test.go | 7 ++- internal/store/postgres/asset_repository.go | 25 ++++---- .../store/postgres/asset_repository_test.go | 63 ++++++++++++++++--- internal/workermanager/discovery_worker.go | 3 +- pkg/queryexpr/es_expr_test.go | 4 +- 7 files changed, 79 insertions(+), 28 deletions(-) diff --git a/core/asset/delete_asset_expr_test.go b/core/asset/delete_asset_expr_test.go index 5332acf0..395a583f 100644 --- a/core/asset/delete_asset_expr_test.go +++ b/core/asset/delete_asset_expr_test.go @@ -33,7 +33,7 @@ func TestDeleteAssetExpr_ToQuery(t *testing.T) { exprStr: asset.DeleteAssetExpr{ ExprStr: &esExpr, }, - want: `{"query":{"bool":{"should":[{"term":{"name":"John"}},{"bool":{"must_not":[{"terms":{"service":["test1","test2","test3"]}}]}}]}}}`, + want: `{"query":{"bool":{"should":[{"term":{"name":"John"}},{"bool":{"must_not":[{"terms":{"service.keyword":["test1","test2","test3"]}}]}}]}}}`, wantErr: false, }, { diff --git a/core/asset/service_test.go b/core/asset/service_test.go index a80b3a31..340cde15 100644 --- a/core/asset/service_test.go +++ b/core/asset/service_test.go @@ -448,7 +448,7 @@ func TestService_DeleteAsset(t *testing.T) { func TestService_DeleteAssets(t *testing.T) { dummyRequest := asset.DeleteAssetsRequest{ QueryExpr: `testing < now()`, - DryRun: false, + DryRun: true, } type testCase struct { Description string @@ -478,6 +478,7 @@ func TestService_DeleteAssets(t *testing.T) { ExpectAffectedRows: 11, ExpectErr: nil, }, + // TODO: add case when DryRun = false which regarding goroutine } for _, tc := range testCases { t.Run(tc.Description, func(t *testing.T) { diff --git a/internal/store/elasticsearch/discovery_repository_test.go b/internal/store/elasticsearch/discovery_repository_test.go index 77c57af3..ae492ee7 100644 --- a/internal/store/elasticsearch/discovery_repository_test.go +++ b/internal/store/elasticsearch/discovery_repository_test.go @@ -415,12 +415,13 @@ func TestDiscoveryRepositoryDeleteByQueryExpr(t *testing.T) { }) t.Run("should not return error on success", func(t *testing.T) { + currentTime := time.Now().UTC() ast := asset.Asset{ ID: "delete-id", Type: asset.TypeTable, Service: bigqueryService, URN: "some-urn", - RefreshedAt: time.Now(), + RefreshedAt: ¤tTime, } err = repo.Upsert(ctx, ast) @@ -461,14 +462,14 @@ func TestDiscoveryRepositoryDeleteByQueryExpr(t *testing.T) { Type: asset.TypeTable, Service: bigqueryService, URN: "urn1", - RefreshedAt: currentTime, + RefreshedAt: ¤tTime, } ast2 := asset.Asset{ ID: "id2", Type: asset.TypeTopic, Service: kafkaService, URN: "urn2", - RefreshedAt: currentTime, + RefreshedAt: ¤tTime, } cli, err := esTestServer.NewClient() require.NoError(t, err) diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index 85edb5ac..6090b925 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -614,14 +614,20 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, return asset.InvalidError{AssetID: assetID} } + currentTime := time.Now() + if newAsset.RefreshedAt != nil { + currentTime = *newAsset.RefreshedAt + } + if len(clog) == 0 { - if newAsset.RefreshedAt != oldAsset.RefreshedAt { - return r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { - return r.updateAsset(ctx, tx, assetID, newAsset) - }) + if newAsset.RefreshedAt == nil || newAsset.RefreshedAt == oldAsset.RefreshedAt { + return nil } - return nil + return r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { + newAsset.RefreshedAt = ¤tTime + return r.updateAsset(ctx, tx, assetID, newAsset) + }) } return r.client.RunWithinTx(ctx, func(tx *sqlx.Tx) error { @@ -632,7 +638,8 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, } newAsset.Version = newVersion newAsset.ID = oldAsset.ID - newAsset.UpdatedAt = *newAsset.RefreshedAt // current time + newAsset.UpdatedAt = currentTime + newAsset.RefreshedAt = ¤tTime if err := r.updateAsset(ctx, tx, assetID, newAsset); err != nil { return err @@ -661,10 +668,6 @@ func (r *AssetRepository) update(ctx context.Context, assetID string, newAsset, } func (r *AssetRepository) updateAsset(ctx context.Context, tx *sqlx.Tx, assetID string, newAsset *asset.Asset) error { - currentTime := time.Now() - if newAsset.RefreshedAt != nil { - currentTime = *newAsset.RefreshedAt - } query, args, err := sq.Update("assets"). Set("urn", newAsset.URN). Set("type", newAsset.Type). @@ -675,7 +678,7 @@ func (r *AssetRepository) updateAsset(ctx context.Context, tx *sqlx.Tx, assetID Set("url", newAsset.URL). Set("labels", newAsset.Labels). Set("updated_at", newAsset.UpdatedAt). - Set("refreshed_at", currentTime). + Set("refreshed_at", *newAsset.RefreshedAt). Set("updated_by", newAsset.UpdatedBy.ID). Set("version", newAsset.Version). Where(sq.Eq{"id": assetID}). diff --git a/internal/store/postgres/asset_repository_test.go b/internal/store/postgres/asset_repository_test.go index 075fe1ad..25539376 100644 --- a/internal/store/postgres/asset_repository_test.go +++ b/internal/store/postgres/asset_repository_test.go @@ -705,13 +705,15 @@ func (r *AssetRepositoryTestSuite) TestGetByURN() { } func (r *AssetRepositoryTestSuite) TestVersions() { + currentTime := time.Now().UTC() assetURN := uuid.NewString() + "urn-u-2-version" // v0.1 astVersioning := asset.Asset{ - URN: assetURN, - Type: "table", - Service: "bigquery", - UpdatedBy: r.users[1], + URN: assetURN, + Type: "table", + Service: "bigquery", + UpdatedBy: r.users[1], + RefreshedAt: ¤tTime, } id, err := r.repository.Upsert(r.ctx, &astVersioning) @@ -767,6 +769,7 @@ func (r *AssetRepositoryTestSuite) TestVersions() { Labels: map[string]string{"key1": "value1"}, Version: "0.5", UpdatedBy: r.users[1], + RefreshedAt: ¤tTime, } ast, err := r.repository.GetByID(r.ctx, astVersioning.ID) @@ -797,6 +800,7 @@ func (r *AssetRepositoryTestSuite) TestVersions() { Labels: map[string]string{"key1": "value1"}, Version: "0.5", UpdatedBy: r.users[1], + RefreshedAt: ¤tTime, } ast, err := r.repository.GetByVersionWithID(r.ctx, astVersioning.ID, "0.5") @@ -1066,11 +1070,14 @@ func (r *AssetRepositoryTestSuite) TestUpsert() { r.Run("on update", func() { r.Run("should not create nor updating the asset if asset is identical", func() { + currentTime := time.Now().UTC() ast := asset.Asset{ - URN: "urn-u-2", - Type: "table", - Service: "bigquery", - UpdatedBy: r.users[0], + URN: "urn-u-2", + Type: "table", + Service: "bigquery", + UpdatedBy: r.users[0], + RefreshedAt: ¤tTime, + Version: "0.1", } identicalAsset := ast @@ -1085,15 +1092,52 @@ func (r *AssetRepositoryTestSuite) TestUpsert() { identicalAsset.ID = id r.Equal(ast.ID, identicalAsset.ID) + r.Equal(ast.Version, identicalAsset.Version) + }) + + r.Run("should same asset version if asset only has different in RefreshedAt", func() { + currentTime := time.Now().UTC().AddDate(0, 0, -1) + ast := asset.Asset{ + URN: "urn-u-2", + Type: "table", + Service: "bigquery", + URL: "https://sample-url-old.com", + UpdatedBy: r.users[0], + RefreshedAt: ¤tTime, + Version: "0.1", + } + + id, err := r.repository.Upsert(r.ctx, &ast) + r.Require().NoError(err) + r.NotEmpty(id) + ast.ID = id + + updated := ast + currentTime2 := time.Now().UTC() + updated.RefreshedAt = ¤tTime2 + + id, err = r.repository.Upsert(r.ctx, &updated) + r.Require().NoError(err) + r.NotEmpty(id) + updated.ID = id + + r.Equal(ast.ID, updated.ID) + + actual, err := r.repository.GetByID(r.ctx, ast.ID) + r.NoError(err) + + r.Equal(updated.RefreshedAt, actual.RefreshedAt) + r.Equal(ast.Version, actual.Version) }) - r.Run("should update the asset if asset is not identical", func() { + r.Run("should update the asset version if asset is not identical", func() { ast := asset.Asset{ URN: "urn-u-2", Type: "table", Service: "bigquery", URL: "https://sample-url-old.com", UpdatedBy: r.users[0], + Version: "0.1", } id, err := r.repository.Upsert(r.ctx, &ast) @@ -1115,6 +1159,7 @@ func (r *AssetRepositoryTestSuite) TestUpsert() { r.NoError(err) r.Equal(updated.URL, actual.URL) + r.NotEqual(ast.Version, actual.Version) }) r.Run("should delete old owners if it does not exist on new asset", func() { diff --git a/internal/workermanager/discovery_worker.go b/internal/workermanager/discovery_worker.go index 10b1875e..e3109cc4 100644 --- a/internal/workermanager/discovery_worker.go +++ b/internal/workermanager/discovery_worker.go @@ -4,9 +4,10 @@ import ( "context" "encoding/json" "fmt" + "strings" + "github.com/goto/compass/core/asset" "github.com/goto/compass/pkg/worker" - "strings" ) //go:generate mockery --name=DiscoveryRepository -r --case underscore --with-expecter --structname DiscoveryRepository --filename discovery_repository_mock.go --output=./mocks diff --git a/pkg/queryexpr/es_expr_test.go b/pkg/queryexpr/es_expr_test.go index e4e83890..7ea0b795 100644 --- a/pkg/queryexpr/es_expr_test.go +++ b/pkg/queryexpr/es_expr_test.go @@ -51,13 +51,13 @@ func TestESExpr_ToQuery(t *testing.T) { { name: "in condition", expr: queryexpr.ESExpr(`service in ["test1","test2","test3"]`), - want: `{"query":{"terms":{"service":["test1","test2","test3"]}}}`, + want: `{"query":{"terms":{"service.keyword":["test1","test2","test3"]}}}`, wantErr: false, }, { name: "equals or not in condition", expr: queryexpr.ESExpr(`name == "John" || service not in ["test1","test2","test3"]`), - want: `{"query":{"bool":{"should":[{"term":{"name":"John"}},{"bool":{"must_not":[{"terms":{"service":["test1","test2","test3"]}}]}}]}}}`, + want: `{"query":{"bool":{"should":[{"term":{"name":"John"}},{"bool":{"must_not":[{"terms":{"service.keyword":["test1","test2","test3"]}}]}}]}}}`, wantErr: false, }, { From 600572ec170636e917d6a9a26ec00c32167f7906 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Tue, 20 Aug 2024 17:04:32 +0700 Subject: [PATCH 33/45] test: fix unit test --- .../store/postgres/asset_repository_test.go | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/internal/store/postgres/asset_repository_test.go b/internal/store/postgres/asset_repository_test.go index 25539376..18afbb35 100644 --- a/internal/store/postgres/asset_repository_test.go +++ b/internal/store/postgres/asset_repository_test.go @@ -705,7 +705,7 @@ func (r *AssetRepositoryTestSuite) TestGetByURN() { } func (r *AssetRepositoryTestSuite) TestVersions() { - currentTime := time.Now().UTC() + currentTime := time.Date(2024, time.August, 20, 8, 19, 49, 0, time.UTC) assetURN := uuid.NewString() + "urn-u-2-version" // v0.1 astVersioning := asset.Asset{ @@ -979,16 +979,16 @@ func (r *AssetRepositoryTestSuite) TestVersions() { } func (r *AssetRepositoryTestSuite) TestUpsert() { + refreshedAtTime := time.Date(2024, time.August, 20, 8, 19, 49, 0, time.UTC) r.Run("on insert", func() { r.Run("set ID to asset and version to base version", func() { - currentTime := time.Now() ast := asset.Asset{ URN: "urn-u-1", Type: "table", Service: "bigquery", URL: "https://sample-url.com", UpdatedBy: r.users[0], - RefreshedAt: ¤tTime, + RefreshedAt: &refreshedAtTime, } id, err := r.repository.Upsert(r.ctx, &ast) r.Equal(asset.BaseVersion, ast.Version) @@ -1005,7 +1005,7 @@ func (r *AssetRepositoryTestSuite) TestUpsert() { r.assertAsset(&ast, &assetInDB) ast2 := ast - ast2.RefreshedAt = ¤tTime + ast2.RefreshedAt = &refreshedAtTime ast2.Description = "create a new version" // to force fetch from asset_versions. _, err = r.repository.Upsert(r.ctx, &ast2) r.NoError(err) @@ -1070,13 +1070,12 @@ func (r *AssetRepositoryTestSuite) TestUpsert() { r.Run("on update", func() { r.Run("should not create nor updating the asset if asset is identical", func() { - currentTime := time.Now().UTC() ast := asset.Asset{ URN: "urn-u-2", Type: "table", Service: "bigquery", UpdatedBy: r.users[0], - RefreshedAt: ¤tTime, + RefreshedAt: &refreshedAtTime, Version: "0.1", } identicalAsset := ast @@ -1095,15 +1094,15 @@ func (r *AssetRepositoryTestSuite) TestUpsert() { r.Equal(ast.Version, identicalAsset.Version) }) - r.Run("should same asset version if asset only has different in RefreshedAt", func() { - currentTime := time.Now().UTC().AddDate(0, 0, -1) + r.Run("should same asset version if asset only has different at RefreshedAt", func() { + oneDayAgoRefreshedAtTime := refreshedAtTime.AddDate(0, 0, -1) ast := asset.Asset{ URN: "urn-u-2", Type: "table", Service: "bigquery", URL: "https://sample-url-old.com", UpdatedBy: r.users[0], - RefreshedAt: ¤tTime, + RefreshedAt: &oneDayAgoRefreshedAtTime, Version: "0.1", } @@ -1113,8 +1112,7 @@ func (r *AssetRepositoryTestSuite) TestUpsert() { ast.ID = id updated := ast - currentTime2 := time.Now().UTC() - updated.RefreshedAt = ¤tTime2 + updated.RefreshedAt = &refreshedAtTime id, err = r.repository.Upsert(r.ctx, &updated) r.Require().NoError(err) @@ -1364,15 +1362,15 @@ func (r *AssetRepositoryTestSuite) TestDeleteByURN() { } func (r *AssetRepositoryTestSuite) TestDeleteByQueryExpr() { + refreshedAtTime := time.Date(2024, time.August, 20, 8, 19, 49, 0, time.UTC) r.Run("should delete correct asset", func() { - currentTime := time.Now() - oneYearAgoTime := currentTime.AddDate(-1, 0, 0) + oneYearAgoRefreshedAtTime := refreshedAtTime.AddDate(-1, 0, 0) asset1 := asset.Asset{ URN: "urn-del-1", Type: "table", Service: "bigquery", UpdatedBy: user.User{ID: defaultAssetUpdaterUserID}, - RefreshedAt: &oneYearAgoTime, + RefreshedAt: &oneYearAgoRefreshedAtTime, } asset2 := asset.Asset{ URN: "urn-del-2", @@ -1380,7 +1378,7 @@ func (r *AssetRepositoryTestSuite) TestDeleteByQueryExpr() { Service: "kafka", Version: asset.BaseVersion, UpdatedBy: user.User{ID: defaultAssetUpdaterUserID}, - RefreshedAt: &oneYearAgoTime, + RefreshedAt: &oneYearAgoRefreshedAtTime, } _, err := r.repository.Upsert(r.ctx, &asset1) @@ -1389,7 +1387,7 @@ func (r *AssetRepositoryTestSuite) TestDeleteByQueryExpr() { id, err := r.repository.Upsert(r.ctx, &asset2) r.Require().NoError(err) - query := "refreshed_at <= '" + currentTime.Format("2006-01-02 15:04:05") + + query := "refreshed_at <= '" + refreshedAtTime.Format("2006-01-02T15:04:05Z") + "' && service == '" + asset1.Service + "' && type == '" + asset1.Type.String() + "' && urn == '" + asset1.URN + "'" From 1724260f1f2337ebe20279a6799ded30a27dcaac Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Tue, 20 Aug 2024 17:19:47 +0700 Subject: [PATCH 34/45] refactor: remove unused comment --- internal/store/elasticsearch/discovery_repository.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/store/elasticsearch/discovery_repository.go b/internal/store/elasticsearch/discovery_repository.go index 0c6c4e11..7e38d191 100644 --- a/internal/store/elasticsearch/discovery_repository.go +++ b/internal/store/elasticsearch/discovery_repository.go @@ -145,7 +145,6 @@ func (repo *DiscoveryRepository) DeleteByURN(ctx context.Context, assetURN strin return repo.deleteWithQuery(ctx, "DeleteByURN", fmt.Sprintf(`{"query":{"term":{"urn.keyword": %q}}}`, assetURN)) } -// TODO: Integration testing func (repo *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) error { if strings.TrimSpace(queryExpr) == "" { return asset.ErrEmptyQuery From b1b57108f8ac6985cd30c240568da0dd125c690e Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Wed, 21 Aug 2024 12:11:29 +0700 Subject: [PATCH 35/45] feat: make maxAttemptRetry to be configurable and improve code performance --- cli/server.go | 1 + core/asset/delete_asset_expr.go | 30 +++++------ core/asset/lineage.go | 1 + core/asset/mocks/lineage_repository.go | 43 ++++++++++++++++ core/asset/service.go | 50 +++++++------------ core/asset/service_test.go | 49 +++++++++++++----- internal/server/v1beta1/asset.go | 5 +- internal/server/v1beta1/asset_test.go | 17 ++----- internal/store/postgres/lineage_repository.go | 13 +++-- internal/workermanager/discovery_worker.go | 8 +-- internal/workermanager/worker_manager.go | 41 ++++++++------- mocks/discovery_repository.go | 4 +- .../generic_helper.go | 0 .../generic_helper_test.go | 2 +- pkg/queryexpr/es_expr.go | 1 - 15 files changed, 159 insertions(+), 106 deletions(-) rename pkg/{generic_helper => generichelper}/generic_helper.go (100%) rename pkg/{generic_helper => generichelper}/generic_helper_test.go (97%) diff --git a/cli/server.go b/cli/server.go index c778b882..7f954bd6 100644 --- a/cli/server.go +++ b/cli/server.go @@ -153,6 +153,7 @@ func runServer(ctx context.Context, cfg *Config) error { DiscoveryRepo: discoveryRepository, LineageRepo: lineageRepository, Worker: wrkr, + Logger: logger, }) // init discussion diff --git a/core/asset/delete_asset_expr.go b/core/asset/delete_asset_expr.go index 9d55f279..5b2b8259 100644 --- a/core/asset/delete_asset_expr.go +++ b/core/asset/delete_asset_expr.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - generichelper "github.com/goto/compass/pkg/generic_helper" + "github.com/goto/compass/pkg/generichelper" "github.com/goto/compass/pkg/queryexpr" ) @@ -35,13 +35,13 @@ func (d DeleteAssetExpr) Validate() error { return d.isAllIdentifiersExistInStruct(identifiersWithOperator) } -func (DeleteAssetExpr) isAllIdentifiersExistInStruct(identifiersWithOperator map[string]string) error { - identifiers := generichelper.GetMapKeys(identifiersWithOperator) - for _, identifier := range identifiers { - isFieldValid := generichelper.Contains(assetJSONTagsSchema, identifier) - if !isFieldValid { - return fmt.Errorf("%s is not a valid identifier", identifier) - } +func (DeleteAssetExpr) isRequiredIdentifiersExist(identifiersWithOperator map[string]string) error { + isExist := func(jsonTag string) bool { + return identifiersWithOperator[jsonTag] != "" + } + mustExist := isExist("refreshed_at") && isExist("type") && isExist("service") + if !mustExist { + return fmt.Errorf("must exists these identifiers: refreshed_at, type, and service") } return nil } @@ -56,13 +56,13 @@ func (DeleteAssetExpr) isUsingRightOperator(identifiersWithOperator map[string]s return nil } -func (DeleteAssetExpr) isRequiredIdentifiersExist(identifiersWithOperator map[string]string) error { - isExist := func(jsonTag string) bool { - return identifiersWithOperator[jsonTag] != "" - } - mustExist := isExist("refreshed_at") && isExist("type") && isExist("service") - if !mustExist { - return fmt.Errorf("must exists these identifiers: refreshed_at, type, and service") +func (DeleteAssetExpr) isAllIdentifiersExistInStruct(identifiersWithOperator map[string]string) error { + identifiers := generichelper.GetMapKeys(identifiersWithOperator) + for _, identifier := range identifiers { + isFieldValid := generichelper.Contains(assetJSONTagsSchema, identifier) + if !isFieldValid { + return fmt.Errorf("%s is not a valid identifier", identifier) + } } return nil } diff --git a/core/asset/lineage.go b/core/asset/lineage.go index 61acded7..4b647cb6 100644 --- a/core/asset/lineage.go +++ b/core/asset/lineage.go @@ -31,6 +31,7 @@ type LineageRepository interface { GetGraph(ctx context.Context, urn string, query LineageQuery) (LineageGraph, error) Upsert(ctx context.Context, urn string, upstreams, downstreams []string) error DeleteByURN(ctx context.Context, urn string) error + DeleteByURNs(ctx context.Context, urns []string) error } type LineageGraph []LineageEdge diff --git a/core/asset/mocks/lineage_repository.go b/core/asset/mocks/lineage_repository.go index 70028ffd..b2ab8829 100644 --- a/core/asset/mocks/lineage_repository.go +++ b/core/asset/mocks/lineage_repository.go @@ -66,6 +66,49 @@ func (_c *LineageRepository_DeleteByURN_Call) RunAndReturn(run func(context.Cont return _c } +// DeleteByURNs provides a mock function with given fields: ctx, urns +func (_m *LineageRepository) DeleteByURNs(ctx context.Context, urns []string) error { + ret := _m.Called(ctx, urns) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, []string) error); ok { + r0 = rf(ctx, urns) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// LineageRepository_DeleteByURNs_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteByURNs' +type LineageRepository_DeleteByURNs_Call struct { + *mock.Call +} + +// DeleteByURNs is a helper method to define mock.On call +// - ctx context.Context +// - urns []string +func (_e *LineageRepository_Expecter) DeleteByURNs(ctx interface{}, urns interface{}) *LineageRepository_DeleteByURNs_Call { + return &LineageRepository_DeleteByURNs_Call{Call: _e.mock.On("DeleteByURNs", ctx, urns)} +} + +func (_c *LineageRepository_DeleteByURNs_Call) Run(run func(ctx context.Context, urns []string)) *LineageRepository_DeleteByURNs_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]string)) + }) + return _c +} + +func (_c *LineageRepository_DeleteByURNs_Call) Return(_a0 error) *LineageRepository_DeleteByURNs_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *LineageRepository_DeleteByURNs_Call) RunAndReturn(run func(context.Context, []string) error) *LineageRepository_DeleteByURNs_Call { + _c.Call.Return(run) + return _c +} + // GetGraph provides a mock function with given fields: ctx, urn, query func (_m *LineageRepository) GetGraph(ctx context.Context, urn string, query asset.LineageQuery) (asset.LineageGraph, error) { ret := _m.Called(ctx, urn, query) diff --git a/core/asset/service.go b/core/asset/service.go index 0ed3e7d7..336399c3 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -3,11 +3,11 @@ package asset import ( "context" "fmt" - "log" "time" "github.com/google/uuid" "github.com/goto/compass/pkg/queryexpr" + "github.com/goto/salt/log" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" @@ -18,6 +18,7 @@ type Service struct { discoveryRepository DiscoveryRepository lineageRepository LineageRepository worker Worker + logger log.Logger assetOpCounter metric.Int64Counter } @@ -37,6 +38,7 @@ type ServiceDeps struct { DiscoveryRepo DiscoveryRepository LineageRepo LineageRepository Worker Worker + Logger log.Logger } func NewService(deps ServiceDeps) *Service { @@ -131,7 +133,7 @@ func (s *Service) DeleteAsset(ctx context.Context, id string) (err error) { func (s *Service) DeleteAssets(ctx context.Context, request DeleteAssetsRequest) (affectedRows uint32, err error) { expr := queryexpr.SQLExpr(request.QueryExpr) - deleteExpr := &DeleteAssetExpr{ + deleteExpr := DeleteAssetExpr{ ExprStr: &expr, } total, err := s.assetRepository.GetCountByQueryExpr(ctx, deleteExpr) @@ -139,46 +141,28 @@ func (s *Service) DeleteAssets(ctx context.Context, request DeleteAssetsRequest) return 0, err } - if request.DryRun { - return uint32(total), nil + if !request.DryRun { + newCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + go s.executeDeleteAssets(newCtx, deleteExpr) } - s.deleteAssetsAsynchronously(request.QueryExpr) - return uint32(total), nil } -func (s *Service) deleteAssetsAsynchronously(queryExpr string) { - expr := queryexpr.SQLExpr(queryExpr) - deleteExpr := DeleteAssetExpr{ - ExprStr: &expr, +func (s *Service) executeDeleteAssets(ctx context.Context, deleteExpr queryexpr.ExprStr) { + deletedURNs, err := s.assetRepository.DeleteByQueryExpr(ctx, deleteExpr) + if err != nil { + s.logger.Error("Asset deletion failed, skipping Elasticsearch and Lineage deletions. Err:", err) + return } - go func() { - urnsDeleted, err := s.assetRepository.DeleteByQueryExpr(context.Background(), deleteExpr) - - // Perform Elasticsearch and Lineage deletion asynchronously ONLY IF the Assets deletion is successful - if err == nil { - go s.deleteAssetsInLineageByQueryExpr(urnsDeleted) - s.deleteAssetsInElasticsearchByQueryExpr(queryExpr) - } else { - // TODO: need to reconsider how to properly handle error - log.Printf("Database deletion failed, skipping Elasticsearch and Lineage deletions. Err: %v", err) - } - }() -} - -func (s *Service) deleteAssetsInElasticsearchByQueryExpr(queryExpr string) { - if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(context.Background(), queryExpr); err != nil { - log.Printf("Error occurred during Elasticsearch deletion: %s", err) + if err := s.lineageRepository.DeleteByURNs(ctx, deletedURNs); err != nil { + s.logger.Error("Error occurred during Lineage deletion:", err) } -} -func (s *Service) deleteAssetsInLineageByQueryExpr(urns []string) { - for _, urn := range urns { - if err := s.lineageRepository.DeleteByURN(context.Background(), urn); err != nil { - log.Printf("Error occurred during Lineage deletion: %s", err) - } + if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(ctx, deleteExpr.String()); err != nil { + s.logger.Error("Error occurred during Elasticsearch deletion:", err) } } diff --git a/core/asset/service_test.go b/core/asset/service_test.go index 340cde15..ec4bbdf6 100644 --- a/core/asset/service_test.go +++ b/core/asset/service_test.go @@ -446,39 +446,60 @@ func TestService_DeleteAsset(t *testing.T) { } func TestService_DeleteAssets(t *testing.T) { - dummyRequest := asset.DeleteAssetsRequest{ + dummyRequestDryRunTrue := asset.DeleteAssetsRequest{ QueryExpr: `testing < now()`, DryRun: true, } + dummyRequestDryRunFalse := asset.DeleteAssetsRequest{ + QueryExpr: `testing < now()`, + DryRun: false, + } type testCase struct { Description string Request asset.DeleteAssetsRequest - Setup func(context.Context, *mocks.AssetRepository, *mocks.DiscoveryRepository, *mocks.LineageRepository) + Setup func(context.Context, *mocks.AssetRepository, *mocks.Worker, *mocks.LineageRepository) ExpectAffectedRows uint32 ExpectErr error } testCases := []testCase{ { - Description: `should return error if query expr not valid`, - Request: dummyRequest, - Setup: func(ctx context.Context, ar *mocks.AssetRepository, _ *mocks.DiscoveryRepository, _ *mocks.LineageRepository) { - ar.EXPECT().GetCountByQueryExpr(ctx, mock.AnythingOfType("*asset.DeleteAssetExpr")). + Description: `should return error if getting affected rows got error`, + Request: dummyRequestDryRunTrue, + Setup: func(ctx context.Context, ar *mocks.AssetRepository, _ *mocks.Worker, _ *mocks.LineageRepository) { + ar.EXPECT().GetCountByQueryExpr(ctx, mock.AnythingOfType("asset.DeleteAssetExpr")). Return(0, errors.New("something wrong")) }, ExpectAffectedRows: 0, ExpectErr: errors.New("something wrong"), }, { - Description: `should only return the numbers of assets that match the given query`, - Request: dummyRequest, - Setup: func(ctx context.Context, ar *mocks.AssetRepository, _ *mocks.DiscoveryRepository, _ *mocks.LineageRepository) { - ar.EXPECT().GetCountByQueryExpr(ctx, mock.AnythingOfType("*asset.DeleteAssetExpr")).Return(11, nil) + Description: `should only return the affected rows that match the given query when getting affected rows successful and dry run is true`, + Request: dummyRequestDryRunTrue, + Setup: func(ctx context.Context, ar *mocks.AssetRepository, _ *mocks.Worker, _ *mocks.LineageRepository) { + ar.EXPECT().GetCountByQueryExpr(ctx, mock.AnythingOfType("asset.DeleteAssetExpr")). + Return(11, nil) }, ExpectAffectedRows: 11, ExpectErr: nil, }, - // TODO: add case when DryRun = false which regarding goroutine + { + Description: `should return the affected rows and perform deletion in the background when getting affected rows successful and dry run is false`, + Request: dummyRequestDryRunFalse, + Setup: func(ctx context.Context, ar *mocks.AssetRepository, w *mocks.Worker, lr *mocks.LineageRepository) { + deletedURNs := []string{"urn1", "urn2"} + ar.EXPECT().GetCountByQueryExpr(ctx, mock.AnythingOfType("asset.DeleteAssetExpr")). + Return(2, nil) + ar.EXPECT().DeleteByQueryExpr(mock.Anything, mock.Anything). + Return(deletedURNs, nil) + lr.EXPECT().DeleteByURNs(mock.Anything, mock.Anything). + Return(nil) + w.EXPECT().EnqueueDeleteAssetsByQueryExprJob(mock.Anything, mock.Anything). + Return(nil) + }, + ExpectAffectedRows: 2, + ExpectErr: nil, + }, } for _, tc := range testCases { t.Run(tc.Description, func(t *testing.T) { @@ -486,19 +507,21 @@ func TestService_DeleteAssets(t *testing.T) { assetRepo := mocks.NewAssetRepository(t) discoveryRepo := mocks.NewDiscoveryRepository(t) + worker := mocks.NewWorker(t) lineageRepo := mocks.NewLineageRepository(t) if tc.Setup != nil { - tc.Setup(ctx, assetRepo, discoveryRepo, lineageRepo) + tc.Setup(ctx, assetRepo, worker, lineageRepo) } svc := asset.NewService(asset.ServiceDeps{ AssetRepo: assetRepo, DiscoveryRepo: discoveryRepo, LineageRepo: lineageRepo, - Worker: workermanager.NewInSituWorker(workermanager.Deps{DiscoveryRepo: discoveryRepo}), + Worker: worker, }) affectedRows, err := svc.DeleteAssets(ctx, tc.Request) + time.Sleep(1 * time.Second) if tc.ExpectErr != nil { assert.ErrorContains(t, err, tc.ExpectErr.Error()) diff --git a/internal/server/v1beta1/asset.go b/internal/server/v1beta1/asset.go index a4dfa4b7..47c2e32c 100644 --- a/internal/server/v1beta1/asset.go +++ b/internal/server/v1beta1/asset.go @@ -313,7 +313,10 @@ func (server *APIServer) DeleteAssets(ctx context.Context, req *compassv1beta1.D return nil, err } defer func() { - server.logger.Warn(fmt.Sprintf("the number of affected rows is %d. delete request: %v", affectedRows, req)) + server.logger.Warn("delete assets", + "the number of affected rows is", affectedRows, + "query delete", req.QueryExpr, + "dry run", req.DryRun) }() deleteAssetsRequest := asset.DeleteAssetsRequest{ diff --git a/internal/server/v1beta1/asset_test.go b/internal/server/v1beta1/asset_test.go index 221c663d..db27651f 100644 --- a/internal/server/v1beta1/asset_test.go +++ b/internal/server/v1beta1/asset_test.go @@ -1004,28 +1004,17 @@ func TestDeleteAssets(t *testing.T) { testCases := []TestCase{ { - Description: "should return error when insert empty query expr", + Description: "should return error when delete assets got error", QueryExpr: dummyQuery, DryRun: false, ExpectStatus: codes.InvalidArgument, ExpectResult: nil, Setup: func(ctx context.Context, as *mocks.AssetService, astID string) { - as.EXPECT().DeleteAssets(ctx, dummyRequest).Return(0, errors.New("error")) + as.EXPECT().DeleteAssets(ctx, dummyRequest).Return(0, errors.New("something wrong")) }, }, { - Description: "should return error when query expr does not meet identifier requirement", - QueryExpr: dummyQuery, - DryRun: false, - ExpectStatus: codes.InvalidArgument, - ExpectResult: nil, - Setup: func(ctx context.Context, as *mocks.AssetService, astID string) { - as.EXPECT().DeleteAssets(ctx, dummyRequest). - Return(0, errors.New("must exist these identifiers: refreshed_at, type, and service. Current identifiers: refreshed_at")) - }, - }, - { - Description: `should only return the numbers of assets that match the given query`, + Description: `should return the affected rows that match the given query when delete assets success`, QueryExpr: dummyQuery, DryRun: false, ExpectStatus: codes.OK, diff --git a/internal/store/postgres/lineage_repository.go b/internal/store/postgres/lineage_repository.go index 10d5c1d3..d5f96fb6 100644 --- a/internal/store/postgres/lineage_repository.go +++ b/internal/store/postgres/lineage_repository.go @@ -49,16 +49,23 @@ func (repo *LineageRepository) GetGraph(ctx context.Context, urn string, query a } func (repo *LineageRepository) DeleteByURN(ctx context.Context, urn string) error { + return repo.DeleteByURNs(ctx, []string{urn}) +} + +func (repo *LineageRepository) DeleteByURNs(ctx context.Context, urns []string) error { qry, args, err := sq.Delete("lineage_graph"). - Where(sq.Or{sq.Eq{"source": urn}, sq.Eq{"target": urn}}). + Where(sq.Or{ + sq.Eq{"source": urns}, + sq.Eq{"target": urns}, + }). PlaceholderFormat(sq.Dollar). ToSql() if err != nil { - return fmt.Errorf("build delete query: URN = '%s': %w", urn, err) + return fmt.Errorf("build delete query: URN = '%v': %w", urns, err) } if _, err := repo.client.db.ExecContext(ctx, qry, args...); err != nil { - return fmt.Errorf("delete asset: URN = '%s': %w", urn, err) + return fmt.Errorf("delete asset: URN = '%v': %w", urns, err) } return nil diff --git a/internal/workermanager/discovery_worker.go b/internal/workermanager/discovery_worker.go index e3109cc4..5a1e7cb0 100644 --- a/internal/workermanager/discovery_worker.go +++ b/internal/workermanager/discovery_worker.go @@ -40,7 +40,7 @@ func (m *Manager) indexAssetHandler() worker.JobHandler { return worker.JobHandler{ Handle: m.IndexAsset, JobOpts: worker.JobOptions{ - MaxAttempts: 3, + MaxAttempts: m.maxAttemptsRetry, Timeout: m.indexTimeout, BackoffStrategy: worker.DefaultExponentialBackoff, }, @@ -51,7 +51,7 @@ func (m *Manager) syncAssetHandler() worker.JobHandler { return worker.JobHandler{ Handle: m.SyncAssets, JobOpts: worker.JobOptions{ - MaxAttempts: 1, + MaxAttempts: m.maxAttemptsRetry, Timeout: m.syncTimeout, BackoffStrategy: worker.DefaultExponentialBackoff, }, @@ -142,7 +142,7 @@ func (m *Manager) deleteAssetHandler() worker.JobHandler { return worker.JobHandler{ Handle: m.DeleteAsset, JobOpts: worker.JobOptions{ - MaxAttempts: 3, + MaxAttempts: m.maxAttemptsRetry, Timeout: m.deleteTimeout, BackoffStrategy: worker.DefaultExponentialBackoff, }, @@ -175,7 +175,7 @@ func (m *Manager) deleteAssetsByQueryHandler() worker.JobHandler { return worker.JobHandler{ Handle: m.DeleteAssetsByQueryExpr, JobOpts: worker.JobOptions{ - MaxAttempts: 3, + MaxAttempts: m.maxAttemptsRetry, Timeout: m.indexTimeout, BackoffStrategy: worker.DefaultExponentialBackoff, }, diff --git a/internal/workermanager/worker_manager.go b/internal/workermanager/worker_manager.go index ed795e52..6a2d9b43 100644 --- a/internal/workermanager/worker_manager.go +++ b/internal/workermanager/worker_manager.go @@ -19,16 +19,17 @@ import ( ) type Manager struct { - processor *pgq.Processor - initDone atomic.Bool - worker Worker - jobManagerPort int - discoveryRepo DiscoveryRepository - assetRepo asset.Repository - logger log.Logger - syncTimeout time.Duration - indexTimeout time.Duration - deleteTimeout time.Duration + processor *pgq.Processor + initDone atomic.Bool + worker Worker + jobManagerPort int + discoveryRepo DiscoveryRepository + assetRepo asset.Repository + logger log.Logger + syncTimeout time.Duration + indexTimeout time.Duration + deleteTimeout time.Duration + maxAttemptsRetry int } //go:generate mockery --name=Worker -r --case underscore --with-expecter --structname Worker --filename worker_mock.go --output=./mocks @@ -50,6 +51,7 @@ type Config struct { SyncJobTimeout time.Duration `mapstructure:"sync_job_timeout" default:"15m"` IndexJobTimeout time.Duration `mapstructure:"index_job_timeout" default:"5s"` DeleteJobTimeout time.Duration `mapstructure:"delete_job_timeout" default:"5s"` + MaxAttemptRetry int `mapstructure:"max_attempt_retry" default:"3"` } type Deps struct { @@ -77,15 +79,16 @@ func New(ctx context.Context, deps Deps) (*Manager, error) { } return &Manager{ - processor: processor, - worker: w, - jobManagerPort: cfg.JobManagerPort, - discoveryRepo: deps.DiscoveryRepo, - assetRepo: deps.AssetRepo, - logger: deps.Logger, - syncTimeout: cfg.SyncJobTimeout, - indexTimeout: cfg.IndexJobTimeout, - deleteTimeout: cfg.DeleteJobTimeout, + processor: processor, + worker: w, + jobManagerPort: cfg.JobManagerPort, + discoveryRepo: deps.DiscoveryRepo, + assetRepo: deps.AssetRepo, + logger: deps.Logger, + syncTimeout: cfg.SyncJobTimeout, + indexTimeout: cfg.IndexJobTimeout, + deleteTimeout: cfg.DeleteJobTimeout, + maxAttemptsRetry: cfg.MaxAttemptRetry, }, nil } diff --git a/mocks/discovery_repository.go b/mocks/discovery_repository.go index 77d5c755..777dbc18 100644 --- a/mocks/discovery_repository.go +++ b/mocks/discovery_repository.go @@ -80,7 +80,7 @@ func (_m *DiscoveryRepository) DeleteByURN(ctx context.Context, assetURN string) return r0 } -// DiscoveryRepository_DeleteByURN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteByURN' +// DiscoveryRepository_DeleteByURN_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DeleteByURNs' type DiscoveryRepository_DeleteByURN_Call struct { *mock.Call } @@ -89,7 +89,7 @@ type DiscoveryRepository_DeleteByURN_Call struct { // - ctx context.Context // - assetURN string func (_e *DiscoveryRepository_Expecter) DeleteByURN(ctx interface{}, assetURN interface{}) *DiscoveryRepository_DeleteByURN_Call { - return &DiscoveryRepository_DeleteByURN_Call{Call: _e.mock.On("DeleteByURN", ctx, assetURN)} + return &DiscoveryRepository_DeleteByURN_Call{Call: _e.mock.On("DeleteByURNs", ctx, assetURN)} } func (_c *DiscoveryRepository_DeleteByURN_Call) Run(run func(ctx context.Context, assetURN string)) *DiscoveryRepository_DeleteByURN_Call { diff --git a/pkg/generic_helper/generic_helper.go b/pkg/generichelper/generic_helper.go similarity index 100% rename from pkg/generic_helper/generic_helper.go rename to pkg/generichelper/generic_helper.go diff --git a/pkg/generic_helper/generic_helper_test.go b/pkg/generichelper/generic_helper_test.go similarity index 97% rename from pkg/generic_helper/generic_helper_test.go rename to pkg/generichelper/generic_helper_test.go index f8004b0c..9227686e 100644 --- a/pkg/generic_helper/generic_helper_test.go +++ b/pkg/generichelper/generic_helper_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - generichelper "github.com/goto/compass/pkg/generic_helper" + "github.com/goto/compass/pkg/generichelper" ) func TestContains(t *testing.T) { diff --git a/pkg/queryexpr/es_expr.go b/pkg/queryexpr/es_expr.go index 4e6c8e9e..dbe79220 100644 --- a/pkg/queryexpr/es_expr.go +++ b/pkg/queryexpr/es_expr.go @@ -9,7 +9,6 @@ import ( var KeywordIdentifiers = [...]string{"service"} -// TODO: Consider not to be pointer type ESExpr string func (e *ESExpr) String() string { From 3a01e1b740b1cff134ba827cdecd621ed76a1263 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Wed, 21 Aug 2024 13:20:44 +0700 Subject: [PATCH 36/45] feat: make cancel for context, and remove pointer in ESExpr and SQLExpr --- cli/server.go | 3 +- core/asset/delete_asset_expr_test.go | 18 +++++----- core/asset/service.go | 18 +++++++--- .../elasticsearch/discovery_repository.go | 4 +-- .../discovery_repository_test.go | 4 +-- .../store/postgres/asset_repository_test.go | 4 +-- pkg/queryexpr/es_expr.go | 34 +++++++++---------- pkg/queryexpr/sql_expr.go | 20 +++++------ 8 files changed, 57 insertions(+), 48 deletions(-) diff --git a/cli/server.go b/cli/server.go index 7f954bd6..f3070021 100644 --- a/cli/server.go +++ b/cli/server.go @@ -148,13 +148,14 @@ func runServer(ctx context.Context, cfg *Config) error { } }() - assetService := asset.NewService(asset.ServiceDeps{ + assetService, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: assetRepository, DiscoveryRepo: discoveryRepository, LineageRepo: lineageRepository, Worker: wrkr, Logger: logger, }) + defer cancel() // init discussion discussionRepository, err := postgres.NewDiscussionRepository(pgClient, 0) diff --git a/core/asset/delete_asset_expr_test.go b/core/asset/delete_asset_expr_test.go index 395a583f..ea24785a 100644 --- a/core/asset/delete_asset_expr_test.go +++ b/core/asset/delete_asset_expr_test.go @@ -23,7 +23,7 @@ func TestDeleteAssetExpr_ToQuery(t *testing.T) { { name: "convert to SQL query", exprStr: asset.DeleteAssetExpr{ - ExprStr: &sqlExpr, + ExprStr: sqlExpr, }, want: "((name = 'John') OR (service NOT IN ('test1', 'test2', 'test3')))", wantErr: false, @@ -31,7 +31,7 @@ func TestDeleteAssetExpr_ToQuery(t *testing.T) { { name: "convert to ES query", exprStr: asset.DeleteAssetExpr{ - ExprStr: &esExpr, + ExprStr: esExpr, }, want: `{"query":{"bool":{"should":[{"term":{"name":"John"}},{"bool":{"must_not":[{"terms":{"service.keyword":["test1","test2","test3"]}}]}}]}}}`, wantErr: false, @@ -39,7 +39,7 @@ func TestDeleteAssetExpr_ToQuery(t *testing.T) { { name: "got error due to wrong syntax", exprStr: asset.DeleteAssetExpr{ - ExprStr: &wrongExpr, + ExprStr: wrongExpr, }, want: "", wantErr: true, @@ -74,7 +74,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { exprStrFn: func() queryexpr.ExprStr { wrongExpr := queryexpr.SQLExpr("findLast(") return asset.DeleteAssetExpr{ - ExprStr: &wrongExpr, + ExprStr: wrongExpr, } }, expectErr: errors.New("error parsing expression"), @@ -85,7 +85,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { exprStrFn: func() queryexpr.ExprStr { missRefreshedAt := queryexpr.SQLExpr(`updated_at < "2023-12-12 23:59:59" && type == "table" && service in ["test1","test2","test3"]`) return asset.DeleteAssetExpr{ - ExprStr: &missRefreshedAt, + ExprStr: missRefreshedAt, } }, expectErr: errors.New("must exists these identifiers: refreshed_at, type, and service"), @@ -96,7 +96,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { exprStrFn: func() queryexpr.ExprStr { missType := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && service in ["test1","test2","test3"]`) return asset.DeleteAssetExpr{ - ExprStr: &missType, + ExprStr: missType, } }, expectErr: errors.New("must exists these identifiers: refreshed_at, type, and service"), @@ -107,7 +107,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { exprStrFn: func() queryexpr.ExprStr { missService := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && type == "table"`) return asset.DeleteAssetExpr{ - ExprStr: &missService, + ExprStr: missService, } }, expectErr: errors.New("must exists these identifiers: refreshed_at, type, and service"), @@ -118,7 +118,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { exprStrFn: func() queryexpr.ExprStr { wrongTypeOperator := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && type != "table" && service in ["test1","test2","test3"]`) return asset.DeleteAssetExpr{ - ExprStr: &wrongTypeOperator, + ExprStr: wrongTypeOperator, } }, expectErr: errors.New("identifier type and service must be equals (==) or IN operator"), @@ -129,7 +129,7 @@ func TestDeleteAssetExpr_Validate(t *testing.T) { exprStrFn: func() queryexpr.ExprStr { wrongServiceOperator := queryexpr.SQLExpr(`refreshed_at < "2023-12-12 23:59:59" && type != "table" && service not in ["test1","test2","test3"]`) return asset.DeleteAssetExpr{ - ExprStr: &wrongServiceOperator, + ExprStr: wrongServiceOperator, } }, expectErr: errors.New("identifier type and service must be equals (==) or IN operator"), diff --git a/core/asset/service.go b/core/asset/service.go index 336399c3..7802d2af 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -19,6 +19,7 @@ type Service struct { lineageRepository LineageRepository worker Worker logger log.Logger + cancelFnList []func() assetOpCounter metric.Int64Counter } @@ -41,21 +42,29 @@ type ServiceDeps struct { Logger log.Logger } -func NewService(deps ServiceDeps) *Service { +func NewService(deps ServiceDeps) (*Service, func()) { assetOpCounter, err := otel.Meter("github.com/goto/compass/core/asset"). Int64Counter("compass.asset.operation") if err != nil { otel.Handle(err) } - return &Service{ + service := &Service{ assetRepository: deps.AssetRepo, discoveryRepository: deps.DiscoveryRepo, lineageRepository: deps.LineageRepo, worker: deps.Worker, + logger: deps.Logger, + cancelFnList: make([]func(), 0), assetOpCounter: assetOpCounter, } + + return service, func() { + for i := range service.cancelFnList { + service.cancelFnList[i]() + } + } } func (s *Service) GetAllAssets(ctx context.Context, flt Filter, withTotal bool) ([]Asset, uint32, error) { @@ -132,9 +141,8 @@ func (s *Service) DeleteAsset(ctx context.Context, id string) (err error) { } func (s *Service) DeleteAssets(ctx context.Context, request DeleteAssetsRequest) (affectedRows uint32, err error) { - expr := queryexpr.SQLExpr(request.QueryExpr) deleteExpr := DeleteAssetExpr{ - ExprStr: &expr, + ExprStr: queryexpr.SQLExpr(request.QueryExpr), } total, err := s.assetRepository.GetCountByQueryExpr(ctx, deleteExpr) if err != nil { @@ -143,7 +151,7 @@ func (s *Service) DeleteAssets(ctx context.Context, request DeleteAssetsRequest) if !request.DryRun { newCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) - defer cancel() + s.cancelFnList = append(s.cancelFnList, cancel) go s.executeDeleteAssets(newCtx, deleteExpr) } diff --git a/internal/store/elasticsearch/discovery_repository.go b/internal/store/elasticsearch/discovery_repository.go index 7e38d191..ba2228b5 100644 --- a/internal/store/elasticsearch/discovery_repository.go +++ b/internal/store/elasticsearch/discovery_repository.go @@ -151,8 +151,8 @@ func (repo *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExp } expr := queryexpr.ESExpr(queryExpr) - deleteAssetESExpr := &asset.DeleteAssetExpr{ - ExprStr: &expr, + deleteAssetESExpr := asset.DeleteAssetExpr{ + ExprStr: expr, } esQuery, err := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) if err != nil { diff --git a/internal/store/elasticsearch/discovery_repository_test.go b/internal/store/elasticsearch/discovery_repository_test.go index ae492ee7..48a6885a 100644 --- a/internal/store/elasticsearch/discovery_repository_test.go +++ b/internal/store/elasticsearch/discovery_repository_test.go @@ -434,8 +434,8 @@ func TestDiscoveryRepositoryDeleteByQueryExpr(t *testing.T) { assert.NoError(t, err) expr := queryexpr.ESExpr(queryExpr) - deleteAssetESExpr := &asset.DeleteAssetExpr{ - ExprStr: &expr, + deleteAssetESExpr := asset.DeleteAssetExpr{ + ExprStr: expr, } esQuery, _ := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) diff --git a/internal/store/postgres/asset_repository_test.go b/internal/store/postgres/asset_repository_test.go index 18afbb35..4a3b7e9a 100644 --- a/internal/store/postgres/asset_repository_test.go +++ b/internal/store/postgres/asset_repository_test.go @@ -1392,8 +1392,8 @@ func (r *AssetRepositoryTestSuite) TestDeleteByQueryExpr() { "' && type == '" + asset1.Type.String() + "' && urn == '" + asset1.URN + "'" sqlExpr := queryexpr.SQLExpr(query) - queryExpr := &asset.DeleteAssetExpr{ - ExprStr: &sqlExpr, + queryExpr := asset.DeleteAssetExpr{ + ExprStr: sqlExpr, } urns, err := r.repository.DeleteByQueryExpr(r.ctx, queryExpr) r.NoError(err) diff --git a/pkg/queryexpr/es_expr.go b/pkg/queryexpr/es_expr.go index dbe79220..4bce9a01 100644 --- a/pkg/queryexpr/es_expr.go +++ b/pkg/queryexpr/es_expr.go @@ -11,12 +11,12 @@ var KeywordIdentifiers = [...]string{"service"} type ESExpr string -func (e *ESExpr) String() string { - return string(*e) +func (e ESExpr) String() string { + return string(e) } // ToQuery default: convert to be Elasticsearch query -func (e *ESExpr) ToQuery() (string, error) { +func (e ESExpr) ToQuery() (string, error) { queryExprParsed, err := getTreeNodeFromQueryExpr(e.String()) if err != nil { return "", err @@ -41,13 +41,13 @@ func (e *ESExpr) ToQuery() (string, error) { } // Validate default: no validation -func (*ESExpr) Validate() error { +func (ESExpr) Validate() error { return nil } // translateToEsQuery The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. // TODO: implement translator for node type that still not covered right now. -func (e *ESExpr) translateToEsQuery(node ast.Node) (interface{}, error) { +func (e ESExpr) translateToEsQuery(node ast.Node) (interface{}, error) { if node == nil { return nil, fmt.Errorf("cannot convert nil to Elasticsearch query") } @@ -86,7 +86,7 @@ func (e *ESExpr) translateToEsQuery(node ast.Node) (interface{}, error) { } } -func (e *ESExpr) binaryNodeToEsQuery(n *ast.BinaryNode) (interface{}, error) { //nolint:gocognit +func (e ESExpr) binaryNodeToEsQuery(n *ast.BinaryNode) (interface{}, error) { //nolint:gocognit left, err := e.translateToEsQuery(n.Left) if err != nil { return nil, err @@ -152,7 +152,7 @@ func (e *ESExpr) binaryNodeToEsQuery(n *ast.BinaryNode) (interface{}, error) { / } } -func (*ESExpr) isKeywordIdentifier(node *ast.IdentifierNode) bool { +func (ESExpr) isKeywordIdentifier(node *ast.IdentifierNode) bool { for _, keyword := range KeywordIdentifiers { if node.Value == keyword { return true @@ -161,7 +161,7 @@ func (*ESExpr) isKeywordIdentifier(node *ast.IdentifierNode) bool { return false } -func (e *ESExpr) unaryNodeToEsQuery(n *ast.UnaryNode) (interface{}, error) { +func (e ESExpr) unaryNodeToEsQuery(n *ast.UnaryNode) (interface{}, error) { switch n.Operator { case "not": if binaryNode, ok := n.Node.(*ast.BinaryNode); ok && binaryNode.Operator == "in" { @@ -198,7 +198,7 @@ func (e *ESExpr) unaryNodeToEsQuery(n *ast.UnaryNode) (interface{}, error) { } } -func (e *ESExpr) arrayNodeToEsQuery(n *ast.ArrayNode) ([]interface{}, error) { +func (e ESExpr) arrayNodeToEsQuery(n *ast.ArrayNode) ([]interface{}, error) { values := make([]interface{}, len(n.Nodes)) for i, node := range n.Nodes { nodeValue, err := e.translateToEsQuery(node) @@ -210,7 +210,7 @@ func (e *ESExpr) arrayNodeToEsQuery(n *ast.ArrayNode) ([]interface{}, error) { return values, nil } -func (*ESExpr) operatorToEsQuery(operator string) string { +func (ESExpr) operatorToEsQuery(operator string) string { switch operator { case ">": return "gt" @@ -225,7 +225,7 @@ func (*ESExpr) operatorToEsQuery(operator string) string { return operator } -func (*ESExpr) boolQuery(condition string, left, right interface{}) map[string]interface{} { +func (ESExpr) boolQuery(condition string, left, right interface{}) map[string]interface{} { return map[string]interface{}{ "bool": map[string]interface{}{ condition: []interface{}{left, right}, @@ -233,7 +233,7 @@ func (*ESExpr) boolQuery(condition string, left, right interface{}) map[string]i } } -func (*ESExpr) termQuery(field string, value interface{}) map[string]interface{} { +func (ESExpr) termQuery(field string, value interface{}) map[string]interface{} { return map[string]interface{}{ "term": map[string]interface{}{ field: value, @@ -241,7 +241,7 @@ func (*ESExpr) termQuery(field string, value interface{}) map[string]interface{} } } -func (*ESExpr) mustNotQuery(field string, value interface{}) map[string]interface{} { +func (ESExpr) mustNotQuery(field string, value interface{}) map[string]interface{} { return map[string]interface{}{ "bool": map[string]interface{}{ "must_not": []interface{}{ @@ -255,7 +255,7 @@ func (*ESExpr) mustNotQuery(field string, value interface{}) map[string]interfac } } -func (*ESExpr) rangeQuery(field, operator string, value interface{}) map[string]interface{} { +func (ESExpr) rangeQuery(field, operator string, value interface{}) map[string]interface{} { return map[string]interface{}{ "range": map[string]interface{}{ field: map[string]interface{}{ @@ -265,7 +265,7 @@ func (*ESExpr) rangeQuery(field, operator string, value interface{}) map[string] } } -func (*ESExpr) termsQuery(field string, values interface{}) map[string]interface{} { +func (ESExpr) termsQuery(field string, values interface{}) map[string]interface{} { return map[string]interface{}{ "terms": map[string]interface{}{ field: values, @@ -273,7 +273,7 @@ func (*ESExpr) termsQuery(field string, values interface{}) map[string]interface } } -func (*ESExpr) mustNotTermsQuery(field string, values interface{}) map[string]interface{} { +func (ESExpr) mustNotTermsQuery(field string, values interface{}) map[string]interface{} { return map[string]interface{}{ "bool": map[string]interface{}{ "must_not": []interface{}{ @@ -287,6 +287,6 @@ func (*ESExpr) mustNotTermsQuery(field string, values interface{}) map[string]in } } -func (*ESExpr) unsupportedQueryError(node ast.Node) error { +func (ESExpr) unsupportedQueryError(node ast.Node) error { return fmt.Errorf("unsupported query expr: %s to Elasticsearch query", node.String()) } diff --git a/pkg/queryexpr/sql_expr.go b/pkg/queryexpr/sql_expr.go index 6f64e2d8..d48966c2 100644 --- a/pkg/queryexpr/sql_expr.go +++ b/pkg/queryexpr/sql_expr.go @@ -10,12 +10,12 @@ import ( type SQLExpr string -func (s *SQLExpr) String() string { - return string(*s) +func (s SQLExpr) String() string { + return string(s) } // ToQuery default -func (s *SQLExpr) ToQuery() (string, error) { +func (s SQLExpr) ToQuery() (string, error) { queryExprParsed, err := getTreeNodeFromQueryExpr(s.String()) if err != nil { return "", err @@ -28,13 +28,13 @@ func (s *SQLExpr) ToQuery() (string, error) { } // Validate default: no validation -func (*SQLExpr) Validate() error { +func (SQLExpr) Validate() error { return nil } // convertToSQL The idea came from ast.Walk. Currently, the development focus implement for the node type that most likely used in our needs. // TODO: implement translator for node type that still not covered right now. -func (s *SQLExpr) convertToSQL(node ast.Node, stringBuilder *strings.Builder) error { +func (s SQLExpr) convertToSQL(node ast.Node, stringBuilder *strings.Builder) error { if node == nil { return fmt.Errorf("cannot convert nil to SQL query") } @@ -83,7 +83,7 @@ func (s *SQLExpr) convertToSQL(node ast.Node, stringBuilder *strings.Builder) er return nil } -func (s *SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings.Builder) error { +func (s SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings.Builder) error { operator := s.operatorToSQL(n) if operator == "" { // most likely the node is operation result, err := GetQueryExprResult(n.String()) @@ -109,7 +109,7 @@ func (s *SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings return nil } -func (s *SQLExpr) arrayNodeToSQLQuery(n *ast.ArrayNode, stringBuilder *strings.Builder) error { +func (s SQLExpr) arrayNodeToSQLQuery(n *ast.ArrayNode, stringBuilder *strings.Builder) error { stringBuilder.WriteString("(") for i := range n.Nodes { if err := s.convertToSQL(n.Nodes[i], stringBuilder); err != nil { @@ -123,7 +123,7 @@ func (s *SQLExpr) arrayNodeToSQLQuery(n *ast.ArrayNode, stringBuilder *strings.B return nil } -func (s *SQLExpr) patchUnaryNode(n *ast.UnaryNode) error { +func (s SQLExpr) patchUnaryNode(n *ast.UnaryNode) error { switch n.Operator { case "not": binaryNode, ok := (n.Node).(*ast.BinaryNode) @@ -161,7 +161,7 @@ func (s *SQLExpr) patchUnaryNode(n *ast.UnaryNode) error { return nil } -func (*SQLExpr) operatorToSQL(bn *ast.BinaryNode) string { +func (SQLExpr) operatorToSQL(bn *ast.BinaryNode) string { switch strings.ToUpper(bn.Operator) { case "&&": return "AND" @@ -186,6 +186,6 @@ func (*SQLExpr) operatorToSQL(bn *ast.BinaryNode) string { return "" // identify operation, like: +, -, *, etc } -func (*SQLExpr) unsupportedQueryError(node ast.Node) error { +func (SQLExpr) unsupportedQueryError(node ast.Node) error { return fmt.Errorf("unsupported query expr: %s to SQL query", node.String()) } From ed71b2809078c0dfc578a50a37d3efaa5866cb56 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Wed, 21 Aug 2024 13:23:16 +0700 Subject: [PATCH 37/45] refactor: make clean as linter suggestion --- core/asset/service.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/asset/service.go b/core/asset/service.go index 7802d2af..cc19d3f7 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -42,14 +42,14 @@ type ServiceDeps struct { Logger log.Logger } -func NewService(deps ServiceDeps) (*Service, func()) { +func NewService(deps ServiceDeps) (service *Service, cancel func()) { assetOpCounter, err := otel.Meter("github.com/goto/compass/core/asset"). Int64Counter("compass.asset.operation") if err != nil { otel.Handle(err) } - service := &Service{ + newService := &Service{ assetRepository: deps.AssetRepo, discoveryRepository: deps.DiscoveryRepo, lineageRepository: deps.LineageRepo, @@ -60,9 +60,9 @@ func NewService(deps ServiceDeps) (*Service, func()) { assetOpCounter: assetOpCounter, } - return service, func() { - for i := range service.cancelFnList { - service.cancelFnList[i]() + return newService, func() { + for i := range newService.cancelFnList { + newService.cancelFnList[i]() } } } From 184041f5a85a69aba284e4ef7167f5ea40ca77bc Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Wed, 21 Aug 2024 13:31:51 +0700 Subject: [PATCH 38/45] test: fix unit test due to cancel func when create new asset service --- core/asset/service_test.go | 42 +++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/core/asset/service_test.go b/core/asset/service_test.go index ec4bbdf6..2ec05e7b 100644 --- a/core/asset/service_test.go +++ b/core/asset/service_test.go @@ -90,11 +90,12 @@ func TestService_GetAllAssets(t *testing.T) { tc.Setup(ctx, mockAssetRepo, mockDiscoveryRepo, mockLineageRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: mockAssetRepo, DiscoveryRepo: mockDiscoveryRepo, LineageRepo: mockLineageRepo, }) + defer cancel() got, cnt, err := svc.GetAllAssets(ctx, tc.Filter, tc.WithTotal) if err != nil && errors.Is(tc.Err, err) { @@ -154,7 +155,8 @@ func TestService_GetTypes(t *testing.T) { tc.Setup(ctx, mockAssetRepo) } - svc := asset.NewService(asset.ServiceDeps{AssetRepo: mockAssetRepo}) + svc, cancel := asset.NewService(asset.ServiceDeps{AssetRepo: mockAssetRepo}) + defer cancel() got, err := svc.GetTypes(ctx, tc.Filter) if err != nil && errors.Is(tc.Err, err) { @@ -239,12 +241,13 @@ func TestService_UpsertAsset(t *testing.T) { tc.Setup(ctx, assetRepo, discoveryRepo, lineageRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: assetRepo, DiscoveryRepo: discoveryRepo, LineageRepo: lineageRepo, Worker: workermanager.NewInSituWorker(workermanager.Deps{DiscoveryRepo: discoveryRepo}), }) + defer cancel() rid, err := svc.UpsertAsset(ctx, tc.Asset, tc.Upstreams, tc.Downstreams) if tc.Err != nil { @@ -303,12 +306,13 @@ func TestService_UpsertAssetWithoutLineage(t *testing.T) { tc.Setup(ctx, assetRepo, discoveryRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: assetRepo, DiscoveryRepo: discoveryRepo, LineageRepo: mocks.NewLineageRepository(t), Worker: workermanager.NewInSituWorker(workermanager.Deps{DiscoveryRepo: discoveryRepo}), }) + defer cancel() rid, err := svc.UpsertAssetWithoutLineage(ctx, tc.Asset) if tc.Err != nil { @@ -430,12 +434,13 @@ func TestService_DeleteAsset(t *testing.T) { tc.Setup(ctx, assetRepo, discoveryRepo, lineageRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: assetRepo, DiscoveryRepo: discoveryRepo, LineageRepo: lineageRepo, Worker: workermanager.NewInSituWorker(workermanager.Deps{DiscoveryRepo: discoveryRepo}), }) + defer cancel() err := svc.DeleteAsset(ctx, tc.ID) if err != nil && errors.Is(tc.Err, err) { @@ -513,12 +518,13 @@ func TestService_DeleteAssets(t *testing.T) { tc.Setup(ctx, assetRepo, worker, lineageRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: assetRepo, DiscoveryRepo: discoveryRepo, LineageRepo: lineageRepo, Worker: worker, }) + defer cancel() affectedRows, err := svc.DeleteAssets(ctx, tc.Request) time.Sleep(1 * time.Second) @@ -640,11 +646,12 @@ func TestService_GetAssetByID(t *testing.T) { tc.Setup(ctx, mockAssetRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: mockAssetRepo, DiscoveryRepo: mocks.NewDiscoveryRepository(t), LineageRepo: mocks.NewLineageRepository(t), }) + defer cancel() actual, err := svc.GetAssetByID(ctx, tc.ID) if tc.Expected != nil { @@ -749,11 +756,12 @@ func TestService_GetAssetByIDWithoutProbes(t *testing.T) { tc.Setup(ctx, mockAssetRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: mockAssetRepo, DiscoveryRepo: mocks.NewDiscoveryRepository(t), LineageRepo: mocks.NewLineageRepository(t), }) + defer cancel() actual, err := svc.GetAssetByIDWithoutProbes(ctx, tc.ID) if tc.Expected != nil { @@ -822,11 +830,12 @@ func TestService_GetAssetByVersion(t *testing.T) { tc.Setup(ctx, mockAssetRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: mockAssetRepo, DiscoveryRepo: mocks.NewDiscoveryRepository(t), LineageRepo: mocks.NewLineageRepository(t), }) + defer cancel() _, err := svc.GetAssetByVersion(ctx, tc.ID, "v0.0.2") if tc.ExpectedErr != nil { @@ -875,11 +884,12 @@ func TestService_GetAssetVersionHistory(t *testing.T) { tc.Setup(ctx, mockAssetRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: mockAssetRepo, DiscoveryRepo: mockDiscoveryRepo, LineageRepo: mockLineageRepo, }) + defer cancel() _, err := svc.GetAssetVersionHistory(ctx, asset.Filter{}, tc.ID) if err != nil && errors.Is(tc.Err, err) { @@ -1061,11 +1071,12 @@ func TestService_GetLineage(t *testing.T) { tc.Setup(ctx, mockAssetRepo, mockDiscoveryRepo, mockLineageRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: mockAssetRepo, DiscoveryRepo: mockDiscoveryRepo, LineageRepo: mockLineageRepo, }) + defer cancel() actual, err := svc.GetLineage(ctx, "urn-source-1", tc.Query) if tc.Err == nil { @@ -1135,11 +1146,12 @@ func TestService_SearchSuggestGroupAssets(t *testing.T) { tc.Setup(ctx, mockDiscoveryRepo) } - svc := asset.NewService(asset.ServiceDeps{ + svc, cancel := asset.NewService(asset.ServiceDeps{ AssetRepo: mockAssetRepo, DiscoveryRepo: mockDiscoveryRepo, LineageRepo: mockLineageRepo, }) + defer cancel() _, err := svc.SearchAssets(ctx, asset.SearchConfig{}) if err != nil && !assert.Equal(t, tc.ErrSearch, err) { @@ -1171,7 +1183,8 @@ func TestService_CreateAssetProbe(t *testing.T) { mockAssetRepo := mocks.NewAssetRepository(t) mockAssetRepo.EXPECT().AddProbe(ctx, assetURN, &probe).Return(nil) - svc := asset.NewService(asset.ServiceDeps{AssetRepo: mockAssetRepo}) + svc, cancel := asset.NewService(asset.ServiceDeps{AssetRepo: mockAssetRepo}) + defer cancel() err := svc.AddProbe(ctx, assetURN, &probe) assert.NoError(t, err) @@ -1183,7 +1196,8 @@ func TestService_CreateAssetProbe(t *testing.T) { mockAssetRepo := mocks.NewAssetRepository(t) mockAssetRepo.EXPECT().AddProbe(ctx, assetURN, &probe).Return(expectedErr) - svc := asset.NewService(asset.ServiceDeps{AssetRepo: mockAssetRepo}) + svc, cancel := asset.NewService(asset.ServiceDeps{AssetRepo: mockAssetRepo}) + defer cancel() err := svc.AddProbe(ctx, assetURN, &probe) assert.Equal(t, expectedErr, err) From f2ab1c3aa1698ce34eb9736960eaf86e9569c5fb Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Wed, 21 Aug 2024 14:29:49 +0700 Subject: [PATCH 39/45] refactor: change argument of DeleteByQueryExpr in discovery repository and in situ worker --- core/asset/discovery.go | 3 ++- core/asset/mocks/discovery_repository.go | 14 +++++++------ core/asset/service.go | 12 +++++------ .../elasticsearch/discovery_repository.go | 10 +++------ .../discovery_repository_test.go | 21 ++++++++++++------- internal/workermanager/discovery_worker.go | 10 ++++++--- .../workermanager/discovery_worker_test.go | 10 ++++++--- internal/workermanager/in_situ_worker.go | 6 +++++- internal/workermanager/in_situ_worker_test.go | 8 +++++-- .../mocks/discovery_repository_mock.go | 14 +++++++------ pkg/queryexpr/es_expr.go | 3 ++- 11 files changed, 67 insertions(+), 44 deletions(-) diff --git a/core/asset/discovery.go b/core/asset/discovery.go index e9ad90b1..3498a023 100644 --- a/core/asset/discovery.go +++ b/core/asset/discovery.go @@ -3,13 +3,14 @@ package asset //go:generate mockery --name=DiscoveryRepository -r --case underscore --with-expecter --structname DiscoveryRepository --filename discovery_repository.go --output=./mocks import ( "context" + "github.com/goto/compass/pkg/queryexpr" ) type DiscoveryRepository interface { Upsert(context.Context, Asset) error DeleteByID(ctx context.Context, assetID string) error DeleteByURN(ctx context.Context, assetURN string) error - DeleteByQueryExpr(ctx context.Context, queryExpr string) error + DeleteByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) error Search(ctx context.Context, cfg SearchConfig) (results []SearchResult, err error) Suggest(ctx context.Context, cfg SearchConfig) (suggestions []string, err error) GroupAssets(ctx context.Context, cfg GroupConfig) (results []GroupResult, err error) diff --git a/core/asset/mocks/discovery_repository.go b/core/asset/mocks/discovery_repository.go index 465c31d3..3c1d50be 100644 --- a/core/asset/mocks/discovery_repository.go +++ b/core/asset/mocks/discovery_repository.go @@ -8,6 +8,8 @@ import ( asset "github.com/goto/compass/core/asset" mock "github.com/stretchr/testify/mock" + + queryexpr "github.com/goto/compass/pkg/queryexpr" ) // DiscoveryRepository is an autogenerated mock type for the DiscoveryRepository type @@ -67,11 +69,11 @@ func (_c *DiscoveryRepository_DeleteByID_Call) RunAndReturn(run func(context.Con } // DeleteByQueryExpr provides a mock function with given fields: ctx, queryExpr -func (_m *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) error { +func (_m *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) error { ret := _m.Called(ctx, queryExpr) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, queryexpr.ExprStr) error); ok { r0 = rf(ctx, queryExpr) } else { r0 = ret.Error(0) @@ -87,14 +89,14 @@ type DiscoveryRepository_DeleteByQueryExpr_Call struct { // DeleteByQueryExpr is a helper method to define mock.On call // - ctx context.Context -// - queryExpr string +// - queryExpr queryexpr.ExprStr func (_e *DiscoveryRepository_Expecter) DeleteByQueryExpr(ctx interface{}, queryExpr interface{}) *DiscoveryRepository_DeleteByQueryExpr_Call { return &DiscoveryRepository_DeleteByQueryExpr_Call{Call: _e.mock.On("DeleteByQueryExpr", ctx, queryExpr)} } -func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, queryExpr string)) *DiscoveryRepository_DeleteByQueryExpr_Call { +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, queryExpr queryexpr.ExprStr)) *DiscoveryRepository_DeleteByQueryExpr_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) + run(args[0].(context.Context), args[1].(queryexpr.ExprStr)) }) return _c } @@ -104,7 +106,7 @@ func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Return(_a0 error) *Discove return _c } -func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) RunAndReturn(run func(context.Context, string) error) *DiscoveryRepository_DeleteByQueryExpr_Call { +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) RunAndReturn(run func(context.Context, queryexpr.ExprStr) error) *DiscoveryRepository_DeleteByQueryExpr_Call { _c.Call.Return(run) return _c } diff --git a/core/asset/service.go b/core/asset/service.go index cc19d3f7..05406436 100644 --- a/core/asset/service.go +++ b/core/asset/service.go @@ -141,10 +141,10 @@ func (s *Service) DeleteAsset(ctx context.Context, id string) (err error) { } func (s *Service) DeleteAssets(ctx context.Context, request DeleteAssetsRequest) (affectedRows uint32, err error) { - deleteExpr := DeleteAssetExpr{ + deleteSQLExpr := DeleteAssetExpr{ ExprStr: queryexpr.SQLExpr(request.QueryExpr), } - total, err := s.assetRepository.GetCountByQueryExpr(ctx, deleteExpr) + total, err := s.assetRepository.GetCountByQueryExpr(ctx, deleteSQLExpr) if err != nil { return 0, err } @@ -152,14 +152,14 @@ func (s *Service) DeleteAssets(ctx context.Context, request DeleteAssetsRequest) if !request.DryRun { newCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) s.cancelFnList = append(s.cancelFnList, cancel) - go s.executeDeleteAssets(newCtx, deleteExpr) + go s.executeDeleteAssets(newCtx, deleteSQLExpr) } return uint32(total), nil } -func (s *Service) executeDeleteAssets(ctx context.Context, deleteExpr queryexpr.ExprStr) { - deletedURNs, err := s.assetRepository.DeleteByQueryExpr(ctx, deleteExpr) +func (s *Service) executeDeleteAssets(ctx context.Context, deleteSQLExpr queryexpr.ExprStr) { + deletedURNs, err := s.assetRepository.DeleteByQueryExpr(ctx, deleteSQLExpr) if err != nil { s.logger.Error("Asset deletion failed, skipping Elasticsearch and Lineage deletions. Err:", err) return @@ -169,7 +169,7 @@ func (s *Service) executeDeleteAssets(ctx context.Context, deleteExpr queryexpr. s.logger.Error("Error occurred during Lineage deletion:", err) } - if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(ctx, deleteExpr.String()); err != nil { + if err := s.worker.EnqueueDeleteAssetsByQueryExprJob(ctx, deleteSQLExpr.String()); err != nil { s.logger.Error("Error occurred during Elasticsearch deletion:", err) } } diff --git a/internal/store/elasticsearch/discovery_repository.go b/internal/store/elasticsearch/discovery_repository.go index ba2228b5..60b11629 100644 --- a/internal/store/elasticsearch/discovery_repository.go +++ b/internal/store/elasticsearch/discovery_repository.go @@ -145,16 +145,12 @@ func (repo *DiscoveryRepository) DeleteByURN(ctx context.Context, assetURN strin return repo.deleteWithQuery(ctx, "DeleteByURN", fmt.Sprintf(`{"query":{"term":{"urn.keyword": %q}}}`, assetURN)) } -func (repo *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) error { - if strings.TrimSpace(queryExpr) == "" { +func (repo *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) error { + if strings.TrimSpace(queryExpr.String()) == "" { return asset.ErrEmptyQuery } - expr := queryexpr.ESExpr(queryExpr) - deleteAssetESExpr := asset.DeleteAssetExpr{ - ExprStr: expr, - } - esQuery, err := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) + esQuery, err := queryexpr.ValidateAndGetQueryFromExpr(queryExpr) if err != nil { return err } diff --git a/internal/store/elasticsearch/discovery_repository_test.go b/internal/store/elasticsearch/discovery_repository_test.go index 48a6885a..3a771ed0 100644 --- a/internal/store/elasticsearch/discovery_repository_test.go +++ b/internal/store/elasticsearch/discovery_repository_test.go @@ -410,7 +410,10 @@ func TestDiscoveryRepositoryDeleteByQueryExpr(t *testing.T) { repo := store.NewDiscoveryRepository(esClient, log.NewNoop(), time.Second*10, []string{"number", "id"}) t.Run("should return error if the given query expr is empty", func(t *testing.T) { - err = repo.DeleteByQueryExpr(ctx, "") + queryExpr := asset.DeleteAssetExpr{ + ExprStr: queryexpr.ESExpr(""), + } + err = repo.DeleteByQueryExpr(ctx, queryExpr) assert.ErrorIs(t, err, asset.ErrEmptyQuery) }) @@ -427,17 +430,16 @@ func TestDiscoveryRepositoryDeleteByQueryExpr(t *testing.T) { err = repo.Upsert(ctx, ast) require.NoError(t, err) - queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + query := "refreshed_at <= '" + time.Now().Format("2006-01-02T15:04:05Z") + "' && service == '" + bigqueryService + "' && type == '" + asset.TypeTable.String() + "'" + queryExpr := asset.DeleteAssetExpr{ + ExprStr: queryexpr.ESExpr(query), + } err = repo.DeleteByQueryExpr(ctx, queryExpr) assert.NoError(t, err) - expr := queryexpr.ESExpr(queryExpr) - deleteAssetESExpr := asset.DeleteAssetExpr{ - ExprStr: expr, - } - esQuery, _ := queryexpr.ValidateAndGetQueryFromExpr(deleteAssetESExpr) + esQuery, _ := queryexpr.ValidateAndGetQueryFromExpr(queryExpr) res, err := cli.Search( cli.Search.WithBody(strings.NewReader(esQuery)), @@ -491,9 +493,12 @@ func TestDiscoveryRepositoryDeleteByQueryExpr(t *testing.T) { _, err = cli.Indices.Close([]string{kafkaService}) require.NoError(t, err) - queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + query := "refreshed_at <= '" + time.Now().Format("2006-01-02T15:04:05Z") + "' && service == '" + kafkaService + "' && type == '" + asset.TypeTopic.String() + "'" + queryExpr := asset.DeleteAssetExpr{ + ExprStr: queryexpr.ESExpr(query), + } err = repo.DeleteByQueryExpr(ctx, queryExpr) assert.NoError(t, err) }) diff --git a/internal/workermanager/discovery_worker.go b/internal/workermanager/discovery_worker.go index 5a1e7cb0..d0444da3 100644 --- a/internal/workermanager/discovery_worker.go +++ b/internal/workermanager/discovery_worker.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/goto/compass/core/asset" + "github.com/goto/compass/pkg/queryexpr" "github.com/goto/compass/pkg/worker" ) @@ -15,7 +16,7 @@ import ( type DiscoveryRepository interface { Upsert(context.Context, asset.Asset) error DeleteByURN(ctx context.Context, assetURN string) error - DeleteByQueryExpr(ctx context.Context, queryExpr string) error + DeleteByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) error SyncAssets(ctx context.Context, indexName string) (cleanupFn func() error, err error) } @@ -162,7 +163,7 @@ func (m *Manager) DeleteAsset(ctx context.Context, job worker.JobSpec) error { func (m *Manager) EnqueueDeleteAssetsByQueryExprJob(ctx context.Context, queryExpr string) error { err := m.worker.Enqueue(ctx, worker.JobSpec{ Type: jobDeleteAssetsByQuery, - Payload: ([]byte)(queryExpr), + Payload: []byte(queryExpr), }) if err != nil { return fmt.Errorf("enqueue delete asset job: %w: query expr: '%s'", err, queryExpr) @@ -183,7 +184,10 @@ func (m *Manager) deleteAssetsByQueryHandler() worker.JobHandler { } func (m *Manager) DeleteAssetsByQueryExpr(ctx context.Context, job worker.JobSpec) error { - queryExpr := (string)(job.Payload) + query := (string)(job.Payload) + queryExpr := asset.DeleteAssetExpr{ + ExprStr: queryexpr.ESExpr(query), + } if err := m.discoveryRepo.DeleteByQueryExpr(ctx, queryExpr); err != nil { return &worker.RetryableError{ Cause: fmt.Errorf("delete asset from discovery repo: %w: query expr: '%s'", err, queryExpr), diff --git a/internal/workermanager/discovery_worker_test.go b/internal/workermanager/discovery_worker_test.go index 8cd0f1ca..6fcda99f 100644 --- a/internal/workermanager/discovery_worker_test.go +++ b/internal/workermanager/discovery_worker_test.go @@ -9,6 +9,7 @@ import ( "github.com/goto/compass/internal/testutils" "github.com/goto/compass/internal/workermanager" "github.com/goto/compass/internal/workermanager/mocks" + "github.com/goto/compass/pkg/queryexpr" "github.com/goto/compass/pkg/worker" "github.com/stretchr/testify/assert" ) @@ -176,7 +177,7 @@ func TestManager_EnqueueDeleteAssetsByQueryExprJob(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02T15:04:05Z") + "' && service == 'test-service'" + "' && type == 'table'" + "' && urn == 'some-urn'" @@ -214,13 +215,16 @@ func TestManager_DeleteAssets(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02T15:04:05Z") + "' && service == 'test-service'" + "' && type == 'table'" + "' && urn == 'some-urn'" + deleteESExpr := asset.DeleteAssetExpr{ + ExprStr: queryexpr.ESExpr(queryExpr), + } discoveryRepo := mocks.NewDiscoveryRepository(t) discoveryRepo.EXPECT(). - DeleteByQueryExpr(ctx, queryExpr). + DeleteByQueryExpr(ctx, deleteESExpr). Return(tc.discoveryErr) mgr := workermanager.NewWithWorker(mocks.NewWorker(t), workermanager.Deps{ diff --git a/internal/workermanager/in_situ_worker.go b/internal/workermanager/in_situ_worker.go index 8f395e0e..15876044 100644 --- a/internal/workermanager/in_situ_worker.go +++ b/internal/workermanager/in_situ_worker.go @@ -7,6 +7,7 @@ import ( "sync" "github.com/goto/compass/core/asset" + "github.com/goto/compass/pkg/queryexpr" "github.com/goto/salt/log" ) @@ -41,7 +42,10 @@ func (m *InSituWorker) EnqueueDeleteAssetJob(ctx context.Context, urn string) er } func (m *InSituWorker) EnqueueDeleteAssetsByQueryExprJob(ctx context.Context, queryExpr string) error { - if err := m.discoveryRepo.DeleteByQueryExpr(ctx, queryExpr); err != nil { + deleteESExpr := asset.DeleteAssetExpr{ + ExprStr: queryexpr.ESExpr(queryExpr), + } + if err := m.discoveryRepo.DeleteByQueryExpr(ctx, deleteESExpr); err != nil { return fmt.Errorf("delete asset from discovery repo: %w: query expr: '%s'", err, queryExpr) } return nil diff --git a/internal/workermanager/in_situ_worker_test.go b/internal/workermanager/in_situ_worker_test.go index d8a745d8..2ea793a0 100644 --- a/internal/workermanager/in_situ_worker_test.go +++ b/internal/workermanager/in_situ_worker_test.go @@ -8,6 +8,7 @@ import ( "github.com/goto/compass/core/asset" "github.com/goto/compass/internal/workermanager" "github.com/goto/compass/internal/workermanager/mocks" + "github.com/goto/compass/pkg/queryexpr" "github.com/stretchr/testify/assert" ) @@ -96,13 +97,16 @@ func TestInSituWorker_EnqueueDeleteAssetsByQueryExprJob(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02 15:04:05") + + queryExpr := "refreshed_at <= '" + time.Now().Format("2006-01-02T15:04:05Z") + "' && service == 'test-service'" + "' && type == 'table'" + "' && urn == 'some-urn'" + deleteESExpr := asset.DeleteAssetExpr{ + ExprStr: queryexpr.ESExpr(queryExpr), + } discoveryRepo := mocks.NewDiscoveryRepository(t) discoveryRepo.EXPECT(). - DeleteByQueryExpr(ctx, queryExpr). + DeleteByQueryExpr(ctx, deleteESExpr). Return(tc.discoveryErr) wrkr := workermanager.NewInSituWorker(workermanager.Deps{ diff --git a/internal/workermanager/mocks/discovery_repository_mock.go b/internal/workermanager/mocks/discovery_repository_mock.go index ca07cb45..70728d01 100644 --- a/internal/workermanager/mocks/discovery_repository_mock.go +++ b/internal/workermanager/mocks/discovery_repository_mock.go @@ -8,6 +8,8 @@ import ( asset "github.com/goto/compass/core/asset" mock "github.com/stretchr/testify/mock" + + queryexpr "github.com/goto/compass/pkg/queryexpr" ) // DiscoveryRepository is an autogenerated mock type for the DiscoveryRepository type @@ -24,11 +26,11 @@ func (_m *DiscoveryRepository) EXPECT() *DiscoveryRepository_Expecter { } // DeleteByQueryExpr provides a mock function with given fields: ctx, queryExpr -func (_m *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr string) error { +func (_m *DiscoveryRepository) DeleteByQueryExpr(ctx context.Context, queryExpr queryexpr.ExprStr) error { ret := _m.Called(ctx, queryExpr) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, queryexpr.ExprStr) error); ok { r0 = rf(ctx, queryExpr) } else { r0 = ret.Error(0) @@ -44,14 +46,14 @@ type DiscoveryRepository_DeleteByQueryExpr_Call struct { // DeleteByQueryExpr is a helper method to define mock.On call // - ctx context.Context -// - queryExpr string +// - queryExpr queryexpr.ExprStr func (_e *DiscoveryRepository_Expecter) DeleteByQueryExpr(ctx interface{}, queryExpr interface{}) *DiscoveryRepository_DeleteByQueryExpr_Call { return &DiscoveryRepository_DeleteByQueryExpr_Call{Call: _e.mock.On("DeleteByQueryExpr", ctx, queryExpr)} } -func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, queryExpr string)) *DiscoveryRepository_DeleteByQueryExpr_Call { +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Run(run func(ctx context.Context, queryExpr queryexpr.ExprStr)) *DiscoveryRepository_DeleteByQueryExpr_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) + run(args[0].(context.Context), args[1].(queryexpr.ExprStr)) }) return _c } @@ -61,7 +63,7 @@ func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) Return(_a0 error) *Discove return _c } -func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) RunAndReturn(run func(context.Context, string) error) *DiscoveryRepository_DeleteByQueryExpr_Call { +func (_c *DiscoveryRepository_DeleteByQueryExpr_Call) RunAndReturn(run func(context.Context, queryexpr.ExprStr) error) *DiscoveryRepository_DeleteByQueryExpr_Call { _c.Call.Return(run) return _c } diff --git a/pkg/queryexpr/es_expr.go b/pkg/queryexpr/es_expr.go index 4bce9a01..9c0d804c 100644 --- a/pkg/queryexpr/es_expr.go +++ b/pkg/queryexpr/es_expr.go @@ -2,6 +2,7 @@ package queryexpr import ( "encoding/json" + "errors" "fmt" "github.com/expr-lang/expr/ast" @@ -28,7 +29,7 @@ func (e ESExpr) ToQuery() (string, error) { } esQuery, ok := esQueryInterface.(map[string]interface{}) if !ok { - return "", fmt.Errorf("failed to generate Elasticsearch query") + return "", errors.New("failed to generate Elasticsearch query") } esQuery = map[string]interface{}{"query": esQuery} From a55015487d9e04c57723b16945d826c173220126 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Wed, 21 Aug 2024 17:25:08 +0700 Subject: [PATCH 40/45] feat: add condition when the complex query result is time and refactor codes --- pkg/queryexpr/query_expr.go | 5 +++++ pkg/queryexpr/sql_expr.go | 23 ++++++++++++++++------- pkg/queryexpr/sql_expr_test.go | 8 ++++++++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/pkg/queryexpr/query_expr.go b/pkg/queryexpr/query_expr.go index 80c90454..879c061a 100644 --- a/pkg/queryexpr/query_expr.go +++ b/pkg/queryexpr/query_expr.go @@ -3,6 +3,7 @@ package queryexpr import ( "fmt" "strings" + "time" "github.com/expr-lang/expr" "github.com/expr-lang/expr/ast" @@ -89,5 +90,9 @@ func GetQueryExprResult(fn string) (any, error) { return nil, fmt.Errorf("failed to evaluate function '%s': %w", fn, err) } + if t, ok := result.(time.Time); ok { + return t.Format(time.RFC3339), nil + } + return result, nil } diff --git a/pkg/queryexpr/sql_expr.go b/pkg/queryexpr/sql_expr.go index d48966c2..20a27826 100644 --- a/pkg/queryexpr/sql_expr.go +++ b/pkg/queryexpr/sql_expr.go @@ -71,11 +71,9 @@ func (s SQLExpr) convertToSQL(node ast.Node, stringBuilder *strings.Builder) err return err } case *ast.BuiltinNode, *ast.ConditionalNode: - result, err := GetQueryExprResult(n.String()) - if err != nil { + if err := s.getQueryExprResult(n.String(), stringBuilder); err != nil { return err } - fmt.Fprintf(stringBuilder, "%v", result) default: return s.unsupportedQueryError(n) } @@ -85,12 +83,10 @@ func (s SQLExpr) convertToSQL(node ast.Node, stringBuilder *strings.Builder) err func (s SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings.Builder) error { operator := s.operatorToSQL(n) - if operator == "" { // most likely the node is operation - result, err := GetQueryExprResult(n.String()) - if err != nil { + if operator == "" { // most likely the node is an operation + if err := s.getQueryExprResult(n.String(), stringBuilder); err != nil { return err } - fmt.Fprintf(stringBuilder, "%v", result) } else { stringBuilder.WriteString("(") if err := s.convertToSQL(n.Left, stringBuilder); err != nil { @@ -109,6 +105,19 @@ func (s SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings. return nil } +func (SQLExpr) getQueryExprResult(fn string, stringBuilder *strings.Builder) error { + result, err := GetQueryExprResult(fn) + if err != nil { + return err + } + if str, ok := result.(string); ok { + result = fmt.Sprintf("'%s'", str) + } + + fmt.Fprintf(stringBuilder, "%v", result) + return nil +} + func (s SQLExpr) arrayNodeToSQLQuery(n *ast.ArrayNode, stringBuilder *strings.Builder) error { stringBuilder.WriteString("(") for i := range n.Nodes { diff --git a/pkg/queryexpr/sql_expr_test.go b/pkg/queryexpr/sql_expr_test.go index 3c2a9ac0..96cee837 100644 --- a/pkg/queryexpr/sql_expr_test.go +++ b/pkg/queryexpr/sql_expr_test.go @@ -1,7 +1,9 @@ package queryexpr_test import ( + "fmt" "testing" + "time" "github.com/goto/compass/pkg/queryexpr" ) @@ -66,6 +68,12 @@ func TestSQLExpr_ToQuery(t *testing.T) { want: `((bool_identifier = false) AND (name != 'John'))`, wantErr: false, }, + { + name: "complex query expression that can directly produce a value regarding time", + expr: queryexpr.SQLExpr(`refreshed_at <= (now() - duration('1h'))`), + want: fmt.Sprintf("(refreshed_at <= '%s')", time.Now().Add(-1*time.Hour).Format(time.RFC3339)), + wantErr: false, + }, { name: "complex query expression that can NOT directly produce a value", expr: queryexpr.SQLExpr(`service in filter(assets, .Service startsWith "T")`), From c3fb1434a9b272efb5c9be14c235872cbc98555a Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Wed, 21 Aug 2024 17:31:13 +0700 Subject: [PATCH 41/45] refactor: revise some codes as feedbacks and add test case in es expr test --- compass.yaml.example | 1 + internal/server/v1beta1/asset.go | 2 +- pkg/queryexpr/es_expr_test.go | 8 ++++++++ pkg/queryexpr/sql_expr.go | 2 +- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/compass.yaml.example b/compass.yaml.example index 24e21ea9..cf384c72 100644 --- a/compass.yaml.example +++ b/compass.yaml.example @@ -68,6 +68,7 @@ worker: sync_job_timeout: 15m index_job_timeout: 5s delete_job_timeout: 5s + max_attempt_retry: 3 client: host: localhost:8081 diff --git a/internal/server/v1beta1/asset.go b/internal/server/v1beta1/asset.go index 47c2e32c..32b89be0 100644 --- a/internal/server/v1beta1/asset.go +++ b/internal/server/v1beta1/asset.go @@ -313,7 +313,7 @@ func (server *APIServer) DeleteAssets(ctx context.Context, req *compassv1beta1.D return nil, err } defer func() { - server.logger.Warn("delete assets", + server.logger.Warn("delete assets by query", "the number of affected rows is", affectedRows, "query delete", req.QueryExpr, "dry run", req.DryRun) diff --git a/pkg/queryexpr/es_expr_test.go b/pkg/queryexpr/es_expr_test.go index 7ea0b795..67209952 100644 --- a/pkg/queryexpr/es_expr_test.go +++ b/pkg/queryexpr/es_expr_test.go @@ -1,7 +1,9 @@ package queryexpr_test import ( + "fmt" "testing" + "time" "github.com/goto/compass/pkg/queryexpr" ) @@ -66,6 +68,12 @@ func TestESExpr_ToQuery(t *testing.T) { want: `{"query":{"term":{"bool_identifier":false}}}`, wantErr: false, }, + { + name: "complex query expression that can directly produce a value regarding time", + expr: queryexpr.ESExpr(`refreshed_at <= (now() - duration('1h'))`), + want: fmt.Sprintf(`{"query":{"range":{"refreshed_at":{"lte":"%v"}}}}`, time.Now().Add(-1*time.Hour).Format(time.RFC3339)), + wantErr: false, + }, { name: "complex query expression that can NOT directly produce a value", expr: queryexpr.ESExpr(`service in filter(assets, .Service startsWith "T")`), diff --git a/pkg/queryexpr/sql_expr.go b/pkg/queryexpr/sql_expr.go index 20a27826..8b1d5fe0 100644 --- a/pkg/queryexpr/sql_expr.go +++ b/pkg/queryexpr/sql_expr.go @@ -14,7 +14,7 @@ func (s SQLExpr) String() string { return string(s) } -// ToQuery default +// ToQuery default: convert to be SQL query func (s SQLExpr) ToQuery() (string, error) { queryExprParsed, err := getTreeNodeFromQueryExpr(s.String()) if err != nil { From ed7b8efe6268092dbb068db0837972098bf8d050 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Wed, 21 Aug 2024 18:16:32 +0700 Subject: [PATCH 42/45] refactor: clean code as feedbacks --- core/asset/delete_asset_expr.go | 11 +++-- internal/server/v1beta1/asset.go | 2 +- internal/store/postgres/asset_repository.go | 10 +--- .../store/postgres/lineage_repository_test.go | 34 ++++++++++++++ pkg/generichelper/generic_helper.go | 6 +-- pkg/queryexpr/es_expr.go | 17 ++++--- pkg/queryexpr/es_expr_test.go | 7 +-- pkg/queryexpr/query_expr.go | 47 +++++++++++++------ pkg/queryexpr/query_expr_test.go | 40 ---------------- pkg/queryexpr/sql_expr.go | 6 +-- pkg/queryexpr/sql_expr_test.go | 4 +- 11 files changed, 95 insertions(+), 89 deletions(-) diff --git a/core/asset/delete_asset_expr.go b/core/asset/delete_asset_expr.go index 5b2b8259..b4477249 100644 --- a/core/asset/delete_asset_expr.go +++ b/core/asset/delete_asset_expr.go @@ -1,6 +1,7 @@ package asset import ( + "errors" "fmt" "strings" @@ -8,7 +9,11 @@ import ( "github.com/goto/compass/pkg/queryexpr" ) -var assetJSONTagsSchema = generichelper.GetJSONTags(Asset{}) +var ( + assetJSONTagsSchema = generichelper.GetJSONTags(Asset{}) + errTypeOrServiceHasWrongOperator = errors.New("identifier type and service must be equals (==) or IN operator") + errMissRequiredIdentifier = errors.New("must exists these identifiers: refreshed_at, type, and service") +) type DeleteAssetExpr struct { queryexpr.ExprStr @@ -41,7 +46,7 @@ func (DeleteAssetExpr) isRequiredIdentifiersExist(identifiersWithOperator map[st } mustExist := isExist("refreshed_at") && isExist("type") && isExist("service") if !mustExist { - return fmt.Errorf("must exists these identifiers: refreshed_at, type, and service") + return errMissRequiredIdentifier } return nil } @@ -51,7 +56,7 @@ func (DeleteAssetExpr) isUsingRightOperator(identifiersWithOperator map[string]s return identifiersWithOperator[jsonTag] == "==" || strings.ToUpper(identifiersWithOperator[jsonTag]) == "IN" } if !isOperatorEqualsOrIn("type") || !isOperatorEqualsOrIn("service") { - return fmt.Errorf("identifier type and service must be equals (==) or IN operator") + return errTypeOrServiceHasWrongOperator } return nil } diff --git a/internal/server/v1beta1/asset.go b/internal/server/v1beta1/asset.go index 32b89be0..5be91565 100644 --- a/internal/server/v1beta1/asset.go +++ b/internal/server/v1beta1/asset.go @@ -314,7 +314,7 @@ func (server *APIServer) DeleteAssets(ctx context.Context, req *compassv1beta1.D } defer func() { server.logger.Warn("delete assets by query", - "the number of affected rows is", affectedRows, + "affected rows", affectedRows, "query delete", req.QueryExpr, "dry run", req.DryRun) }() diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index 6090b925..17753cf3 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "strings" "time" @@ -18,8 +17,6 @@ import ( "github.com/r3labs/diff/v2" ) -const batchSize = 1000 - // AssetRepository is a type that manages user operation to the primary database type AssetRepository struct { client *Client @@ -395,18 +392,13 @@ func (r *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr query urns, err = r.deleteByQueryAndReturnURNS(ctx, query) if err != nil { - log.Printf("Failed to delete by query expr: %v", err) return err } return nil }) - if err != nil { - return nil, err - } - - return urns, nil + return urns, err } // deleteByQueryAndReturnURNS remove all assets that match to query and return array of urn of asset that deleted. diff --git a/internal/store/postgres/lineage_repository_test.go b/internal/store/postgres/lineage_repository_test.go index 271f7abb..52f282b2 100644 --- a/internal/store/postgres/lineage_repository_test.go +++ b/internal/store/postgres/lineage_repository_test.go @@ -119,6 +119,40 @@ func (r *LineageRepositoryTestSuite) TestDeleteByURN() { }) } +func (r *LineageRepositoryTestSuite) TestDeleteByURNs() { + r.Run("should delete assets from lineage", func() { + nodeURN1a := "table-1a" + nodeURN1b := "table-1b" + nodeURNs := []string{nodeURN1a, nodeURN1b} + + // create initial + err := r.repository.Upsert(r.ctx, nodeURN1a, []string{"table-2"}, []string{"table-3"}) + r.NoError(err) + err = r.repository.Upsert(r.ctx, nodeURN1b, []string{"table-2"}, []string{"table-3"}) + r.NoError(err) + + err = r.repository.DeleteByURNs(r.ctx, nodeURNs) + r.NoError(err) + + graph, err := r.repository.GetGraph(r.ctx, nodeURN1a, asset.LineageQuery{}) + r.Require().NoError(err) + r.compareGraphs(asset.LineageGraph{}, graph) + + graph, err = r.repository.GetGraph(r.ctx, nodeURN1b, asset.LineageQuery{}) + r.Require().NoError(err) + r.compareGraphs(asset.LineageGraph{}, graph) + }) + + r.Run("delete when URNs has no lineage", func() { + nodeURN1a := "table-1a" + nodeURN1b := "table-1b" + nodeURNs := []string{nodeURN1a, nodeURN1b} + + err := r.repository.DeleteByURNs(r.ctx, nodeURNs) + r.NoError(err) + }) +} + func (r *LineageRepositoryTestSuite) TestUpsert() { r.Run("should insert all as graph if upstreams and downstreams are new", func() { nodeURN := "table-1" diff --git a/pkg/generichelper/generic_helper.go b/pkg/generichelper/generic_helper.go index 768a0006..58d83b9a 100644 --- a/pkg/generichelper/generic_helper.go +++ b/pkg/generichelper/generic_helper.go @@ -1,12 +1,10 @@ package generichelper -import ( - "reflect" -) +import "reflect" // Contains checks if a target item exists in an array of any type. // -// Example +// Example: // // names := []string{"Alice", "Bob", "Carol"} // result := Contains(names, "Bob") diff --git a/pkg/queryexpr/es_expr.go b/pkg/queryexpr/es_expr.go index 9c0d804c..c177cf77 100644 --- a/pkg/queryexpr/es_expr.go +++ b/pkg/queryexpr/es_expr.go @@ -2,7 +2,6 @@ package queryexpr import ( "encoding/json" - "errors" "fmt" "github.com/expr-lang/expr/ast" @@ -29,7 +28,7 @@ func (e ESExpr) ToQuery() (string, error) { } esQuery, ok := esQueryInterface.(map[string]interface{}) if !ok { - return "", errors.New("failed to generate Elasticsearch query") + return "", errFailedGenerateESQuery } esQuery = map[string]interface{}{"query": esQuery} @@ -50,7 +49,7 @@ func (ESExpr) Validate() error { // TODO: implement translator for node type that still not covered right now. func (e ESExpr) translateToEsQuery(node ast.Node) (interface{}, error) { if node == nil { - return nil, fmt.Errorf("cannot convert nil to Elasticsearch query") + return nil, errCannotConvertNilQuery } switch n := (node).(type) { case *ast.BinaryNode: @@ -77,7 +76,7 @@ func (e ESExpr) translateToEsQuery(node ast.Node) (interface{}, error) { case *ast.ConstantNode: return n.Value, nil case *ast.BuiltinNode, *ast.ConditionalNode: - result, err := GetQueryExprResult(n.String()) + result, err := getQueryExprResult(n.String()) if err != nil { return nil, err } @@ -108,7 +107,7 @@ func (e ESExpr) binaryNodeToEsQuery(n *ast.BinaryNode) (interface{}, error) { // if leftStr, ok := left.(string); ok { return e.termQuery(leftStr, right), nil } - result, err := GetQueryExprResult(n.String()) + result, err := getQueryExprResult(n.String()) if err != nil { return nil, err } @@ -118,7 +117,7 @@ func (e ESExpr) binaryNodeToEsQuery(n *ast.BinaryNode) (interface{}, error) { // if leftStr, ok := left.(string); ok { return e.mustNotQuery(leftStr, right), nil } - result, err := GetQueryExprResult(n.String()) + result, err := getQueryExprResult(n.String()) if err != nil { return nil, err } @@ -128,7 +127,7 @@ func (e ESExpr) binaryNodeToEsQuery(n *ast.BinaryNode) (interface{}, error) { // if leftStr, ok := left.(string); ok { return e.rangeQuery(leftStr, e.operatorToEsQuery(n.Operator), right), nil } - result, err := GetQueryExprResult(n.String()) + result, err := getQueryExprResult(n.String()) if err != nil { return nil, err } @@ -138,14 +137,14 @@ func (e ESExpr) binaryNodeToEsQuery(n *ast.BinaryNode) (interface{}, error) { // if leftStr, ok := left.(string); ok { return e.termsQuery(leftStr, right), nil } - result, err := GetQueryExprResult(n.String()) + result, err := getQueryExprResult(n.String()) if err != nil { return nil, err } return result, nil default: - result, err := GetQueryExprResult(n.String()) + result, err := getQueryExprResult(n.String()) if err != nil { return nil, err } diff --git a/pkg/queryexpr/es_expr_test.go b/pkg/queryexpr/es_expr_test.go index 67209952..b8a7ad37 100644 --- a/pkg/queryexpr/es_expr_test.go +++ b/pkg/queryexpr/es_expr_test.go @@ -69,9 +69,10 @@ func TestESExpr_ToQuery(t *testing.T) { wantErr: false, }, { - name: "complex query expression that can directly produce a value regarding time", - expr: queryexpr.ESExpr(`refreshed_at <= (now() - duration('1h'))`), - want: fmt.Sprintf(`{"query":{"range":{"refreshed_at":{"lte":"%v"}}}}`, time.Now().Add(-1*time.Hour).Format(time.RFC3339)), + name: "complex query expression that can directly produce a value regarding time", + expr: queryexpr.ESExpr(`refreshed_at <= (date("2024-08-21T01:00:00Z") - duration('1h'))`), + want: fmt.Sprintf(`{"query":{"range":{"refreshed_at":{"lte":"%v"}}}}`, + time.Date(2024, 8, 21, 0, 0, 0, 0, time.UTC).Format(time.RFC3339)), wantErr: false, }, { diff --git a/pkg/queryexpr/query_expr.go b/pkg/queryexpr/query_expr.go index 879c061a..c8e9975b 100644 --- a/pkg/queryexpr/query_expr.go +++ b/pkg/queryexpr/query_expr.go @@ -1,6 +1,7 @@ package queryexpr import ( + "errors" "fmt" "strings" "time" @@ -10,17 +11,22 @@ import ( "github.com/expr-lang/expr/parser" ) +var ( + errFailedGenerateESQuery = errors.New("failed to generate Elasticsearch query") + errCannotConvertNilQuery = errors.New("cannot convert nil to query") +) + type ExprStr interface { String() string ToQuery() (string, error) Validate() error } -type ExprVisitor struct { - IdentifiersWithOperator map[string]string // Key: Identifier, Value: Operator +type exprVisitor struct { + identifiersWithOperator map[string]string // Key: Identifier, Value: Operator } -type ExprParam map[string]interface{} +type exprParam map[string]interface{} func ValidateAndGetQueryFromExpr(exprStr ExprStr) (string, error) { if err := exprStr.Validate(); err != nil { @@ -35,24 +41,24 @@ func ValidateAndGetQueryFromExpr(exprStr ExprStr) (string, error) { } // Visit is implementation Visitor interface from expr-lang/expr lib, used by ast.Walk -func (s *ExprVisitor) Visit(node *ast.Node) { //nolint:gocritic +func (s *exprVisitor) Visit(node *ast.Node) { //nolint:gocritic switch n := (*node).(type) { case *ast.BinaryNode: if left, ok := (n.Left).(*ast.IdentifierNode); ok { - s.IdentifiersWithOperator[left.Value] = n.Operator + s.identifiersWithOperator[left.Value] = n.Operator } if right, ok := (n.Right).(*ast.IdentifierNode); ok { - s.IdentifiersWithOperator[right.Value] = n.Operator + s.identifiersWithOperator[right.Value] = n.Operator } case *ast.UnaryNode: if binaryNode, ok := (n.Node).(*ast.BinaryNode); ok { if strings.ToUpper(binaryNode.Operator) == "IN" { notInOperator := "NOT IN" if left, ok := (binaryNode.Left).(*ast.IdentifierNode); ok { - s.IdentifiersWithOperator[left.Value] = notInOperator + s.identifiersWithOperator[left.Value] = notInOperator } if right, ok := (binaryNode.Right).(*ast.IdentifierNode); ok { - s.IdentifiersWithOperator[right.Value] = notInOperator + s.identifiersWithOperator[right.Value] = notInOperator } } } @@ -64,9 +70,9 @@ func GetIdentifiersMap(queryExpr string) (map[string]string, error) { if err != nil { return nil, err } - queryExprVisitor := &ExprVisitor{IdentifiersWithOperator: make(map[string]string)} + queryExprVisitor := &exprVisitor{identifiersWithOperator: make(map[string]string)} ast.Walk(&queryExprParsed, queryExprVisitor) - return queryExprVisitor.IdentifiersWithOperator, nil + return queryExprVisitor.identifiersWithOperator, nil } func getTreeNodeFromQueryExpr(queryExpr string) (ast.Node, error) { @@ -78,16 +84,27 @@ func getTreeNodeFromQueryExpr(queryExpr string) (ast.Node, error) { return parsed.Node, nil } -func GetQueryExprResult(fn string) (any, error) { - env := make(ExprParam) - compile, err := expr.Compile(fn) +// getQueryExprResult used for getting the result of query expr operation. +// The playground can be accessed at https://expr-lang.org/playground +// +// Example: +// +// queryExprOperation := findLast([1, 2, 3, 4], # > 2) +// result := getQueryExprResult(queryExprOperation) +// +// Result: +// +// 4 +func getQueryExprResult(queryExprOperation string) (any, error) { + env := make(exprParam) + compile, err := expr.Compile(queryExprOperation) if err != nil { - return nil, fmt.Errorf("failed to compile function '%s': %w", fn, err) + return nil, fmt.Errorf("failed to compile function '%s': %w", queryExprOperation, err) } result, err := expr.Run(compile, env) if err != nil { - return nil, fmt.Errorf("failed to evaluate function '%s': %w", fn, err) + return nil, fmt.Errorf("failed to evaluate function '%s': %w", queryExprOperation, err) } if t, ok := result.(time.Time); ok { diff --git a/pkg/queryexpr/query_expr_test.go b/pkg/queryexpr/query_expr_test.go index 02c36cfc..0e40d83f 100644 --- a/pkg/queryexpr/query_expr_test.go +++ b/pkg/queryexpr/query_expr_test.go @@ -58,43 +58,3 @@ func TestGetIdentifiersMap(t *testing.T) { }) } } - -func TestGetQueryExprResult(t *testing.T) { - tests := []struct { - name string - expr string - want any - wantErr bool - }{ - { - name: "return a value from func", - expr: `findLast([1, 2, 3, 4], # > 2)`, - want: 4, - wantErr: false, - }, - { - name: "return a value func equation", - expr: `false == !true`, - want: true, - wantErr: false, - }, - { - name: "got error due to can NOT directly produce a value", - expr: `updated_at < '2024-04-05 23:59:59'`, - want: nil, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := queryexpr.GetQueryExprResult(tt.expr) - if (err != nil) != tt.wantErr { - t.Errorf("GetQueryExprResult() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("GetQueryExprResult() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pkg/queryexpr/sql_expr.go b/pkg/queryexpr/sql_expr.go index 8b1d5fe0..0418f865 100644 --- a/pkg/queryexpr/sql_expr.go +++ b/pkg/queryexpr/sql_expr.go @@ -36,7 +36,7 @@ func (SQLExpr) Validate() error { // TODO: implement translator for node type that still not covered right now. func (s SQLExpr) convertToSQL(node ast.Node, stringBuilder *strings.Builder) error { if node == nil { - return fmt.Errorf("cannot convert nil to SQL query") + return errCannotConvertNilQuery } switch n := (node).(type) { case *ast.BinaryNode: @@ -106,7 +106,7 @@ func (s SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings. } func (SQLExpr) getQueryExprResult(fn string, stringBuilder *strings.Builder) error { - result, err := GetQueryExprResult(fn) + result, err := getQueryExprResult(fn) if err != nil { return err } @@ -153,7 +153,7 @@ func (s SQLExpr) patchUnaryNode(n *ast.UnaryNode) error { Value: !nodeV.Value, }) default: - result, err := GetQueryExprResult(n.String()) + result, err := getQueryExprResult(n.String()) if err != nil { return err } diff --git a/pkg/queryexpr/sql_expr_test.go b/pkg/queryexpr/sql_expr_test.go index 96cee837..2ca2e7a9 100644 --- a/pkg/queryexpr/sql_expr_test.go +++ b/pkg/queryexpr/sql_expr_test.go @@ -70,8 +70,8 @@ func TestSQLExpr_ToQuery(t *testing.T) { }, { name: "complex query expression that can directly produce a value regarding time", - expr: queryexpr.SQLExpr(`refreshed_at <= (now() - duration('1h'))`), - want: fmt.Sprintf("(refreshed_at <= '%s')", time.Now().Add(-1*time.Hour).Format(time.RFC3339)), + expr: queryexpr.SQLExpr(`refreshed_at <= (date("2024-08-21T01:00:00Z") - duration('1h'))`), + want: fmt.Sprintf("(refreshed_at <= '%s')", time.Date(2024, 8, 21, 0, 0, 0, 0, time.UTC).Format(time.RFC3339)), wantErr: false, }, { From 8c655b228c100ee3c5d124f61a7e99de304919d3 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Wed, 21 Aug 2024 19:30:34 +0700 Subject: [PATCH 43/45] refactor: add comment so more clear --- pkg/queryexpr/sql_expr.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/queryexpr/sql_expr.go b/pkg/queryexpr/sql_expr.go index 0418f865..23cf40b8 100644 --- a/pkg/queryexpr/sql_expr.go +++ b/pkg/queryexpr/sql_expr.go @@ -71,7 +71,7 @@ func (s SQLExpr) convertToSQL(node ast.Node, stringBuilder *strings.Builder) err return err } case *ast.BuiltinNode, *ast.ConditionalNode: - if err := s.getQueryExprResult(n.String(), stringBuilder); err != nil { + if err := s.getQueryExprResultForSQL(n.String(), stringBuilder); err != nil { return err } default: @@ -84,7 +84,7 @@ func (s SQLExpr) convertToSQL(node ast.Node, stringBuilder *strings.Builder) err func (s SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings.Builder) error { operator := s.operatorToSQL(n) if operator == "" { // most likely the node is an operation - if err := s.getQueryExprResult(n.String(), stringBuilder); err != nil { + if err := s.getQueryExprResultForSQL(n.String(), stringBuilder); err != nil { return err } } else { @@ -105,8 +105,9 @@ func (s SQLExpr) binaryNodeToSQLQuery(n *ast.BinaryNode, stringBuilder *strings. return nil } -func (SQLExpr) getQueryExprResult(fn string, stringBuilder *strings.Builder) error { - result, err := getQueryExprResult(fn) +// getQueryExprResultForSQL using getQueryExprResult to get the result of query expr operation, and make it as SQL syntax +func (SQLExpr) getQueryExprResultForSQL(queryExprOperation string, stringBuilder *strings.Builder) error { + result, err := getQueryExprResult(queryExprOperation) if err != nil { return err } From 7b4254a9cac12da171742e5cb6fe02c758978f5a Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Thu, 22 Aug 2024 11:30:41 +0700 Subject: [PATCH 44/45] refactor: remove redundant code --- internal/store/postgres/asset_repository.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/store/postgres/asset_repository.go b/internal/store/postgres/asset_repository.go index 17753cf3..001c9811 100644 --- a/internal/store/postgres/asset_repository.go +++ b/internal/store/postgres/asset_repository.go @@ -391,11 +391,8 @@ func (r *AssetRepository) DeleteByQueryExpr(ctx context.Context, queryExpr query } urns, err = r.deleteByQueryAndReturnURNS(ctx, query) - if err != nil { - return err - } - return nil + return err }) return urns, err From a1bf8486becf2e91e34eb8adc89c55308ed15ba6 Mon Sep 17 00:00:00 2001 From: Muhammad Luthfi Fahlevi Date: Thu, 22 Aug 2024 14:06:46 +0700 Subject: [PATCH 45/45] feat: add case for MemberNode which handle nested query --- pkg/queryexpr/es_expr.go | 2 ++ pkg/queryexpr/es_expr_test.go | 6 ++++++ pkg/queryexpr/sql_expr.go | 8 ++++++++ pkg/queryexpr/sql_expr_test.go | 6 ++++++ 4 files changed, 22 insertions(+) diff --git a/pkg/queryexpr/es_expr.go b/pkg/queryexpr/es_expr.go index c177cf77..40b743ef 100644 --- a/pkg/queryexpr/es_expr.go +++ b/pkg/queryexpr/es_expr.go @@ -81,6 +81,8 @@ func (e ESExpr) translateToEsQuery(node ast.Node) (interface{}, error) { return nil, err } return result, nil + case *ast.MemberNode: + return n.String(), nil default: return nil, e.unsupportedQueryError(n) } diff --git a/pkg/queryexpr/es_expr_test.go b/pkg/queryexpr/es_expr_test.go index b8a7ad37..c66b98c9 100644 --- a/pkg/queryexpr/es_expr_test.go +++ b/pkg/queryexpr/es_expr_test.go @@ -75,6 +75,12 @@ func TestESExpr_ToQuery(t *testing.T) { time.Date(2024, 8, 21, 0, 0, 0, 0, time.UTC).Format(time.RFC3339)), wantErr: false, }, + { + name: "nested column query", + expr: queryexpr.ESExpr(`foo.bar.abc.def == 'example'`), + want: `{"query":{"term":{"foo.bar.abc.def":"example"}}}`, + wantErr: false, + }, { name: "complex query expression that can NOT directly produce a value", expr: queryexpr.ESExpr(`service in filter(assets, .Service startsWith "T")`), diff --git a/pkg/queryexpr/sql_expr.go b/pkg/queryexpr/sql_expr.go index 23cf40b8..388ce2f4 100644 --- a/pkg/queryexpr/sql_expr.go +++ b/pkg/queryexpr/sql_expr.go @@ -74,6 +74,14 @@ func (s SQLExpr) convertToSQL(node ast.Node, stringBuilder *strings.Builder) err if err := s.getQueryExprResultForSQL(n.String(), stringBuilder); err != nil { return err } + case *ast.MemberNode: + memberIdentifiers := strings.Split(n.String(), ".") + identifier := memberIdentifiers[0] + for i := 1; i <= len(memberIdentifiers)-2; i++ { + identifier += fmt.Sprintf("->'%s'", memberIdentifiers[i]) + } + identifier += fmt.Sprintf("->>'%s'", memberIdentifiers[len(memberIdentifiers)-1]) + stringBuilder.WriteString(identifier) default: return s.unsupportedQueryError(n) } diff --git a/pkg/queryexpr/sql_expr_test.go b/pkg/queryexpr/sql_expr_test.go index 2ca2e7a9..d289d43b 100644 --- a/pkg/queryexpr/sql_expr_test.go +++ b/pkg/queryexpr/sql_expr_test.go @@ -74,6 +74,12 @@ func TestSQLExpr_ToQuery(t *testing.T) { want: fmt.Sprintf("(refreshed_at <= '%s')", time.Date(2024, 8, 21, 0, 0, 0, 0, time.UTC).Format(time.RFC3339)), wantErr: false, }, + { + name: "nested column query", + expr: queryexpr.SQLExpr(`foo.bar.abc.def == 'example'`), + want: `(foo->'bar'->'abc'->>'def' = 'example')`, + wantErr: false, + }, { name: "complex query expression that can NOT directly produce a value", expr: queryexpr.SQLExpr(`service in filter(assets, .Service startsWith "T")`),