diff --git a/api/submitqueue/gateway/proto/gateway.proto b/api/submitqueue/gateway/proto/gateway.proto index 6cbb0cb5..c2d084c5 100644 --- a/api/submitqueue/gateway/proto/gateway.proto +++ b/api/submitqueue/gateway/proto/gateway.proto @@ -146,6 +146,50 @@ message ListResponse { string next_page_token = 2; } +// HistoryByIDRequest selects one retained request history by sqid. +message HistoryByIDRequest { + // Globally unique identifier for the request. + string sqid = 1; +} + +// HistoryEvent is one retained append-only request-log event. +message HistoryEvent { + // Time the request-log entry was created, in Unix milliseconds. + int64 timestamp_ms = 1; + // Customer-friendly request status recorded by the event. + string status = 2; + // Error message associated with the event. Empty when absent. + string last_error = 3; + // Display and debugging metadata associated with the event. + map metadata = 4; +} + +// HistoryByIDResponse contains all retained events for one request. +message HistoryByIDResponse { + // Events ordered by timestamp ascending with a stable storage tie-breaker. + repeated HistoryEvent events = 1; +} + +// HistoryByChangeIDRequest selects retained histories by an exact pinned change URI. +message HistoryByChangeIDRequest { + // Exact change URI supplied in a Land request. + string change_id = 1; +} + +// RequestHistory groups retained events for one request. +message RequestHistory { + // Globally unique request identifier. + string sqid = 1; + // Retained events ordered chronologically. + repeated HistoryEvent events = 2; +} + +// HistoryByChangeIDResponse contains retained histories ordered by numeric sqid counter ascending. +message HistoryByChangeIDResponse { + // Matching request histories. + repeated RequestHistory histories = 1; +} + // *************** // Error messages, returned as `google.rpc.Status` messages. // *************** @@ -209,4 +253,9 @@ service SubmitQueueGateway { // List returns requests received for one queue during a bounded receipt-time range. rpc List(ListRequest) returns (ListResponse) {} + // HistoryByID returns every retained request-log event for one request. + rpc HistoryByID(HistoryByIDRequest) returns (HistoryByIDResponse) {} + + // HistoryByChangeID returns retained request histories for an exact pinned change URI. + rpc HistoryByChangeID(HistoryByChangeIDRequest) returns (HistoryByChangeIDResponse) {} } diff --git a/api/submitqueue/gateway/protopb/gateway.pb.go b/api/submitqueue/gateway/protopb/gateway.pb.go index c5ecdbf9..61fed141 100644 --- a/api/submitqueue/gateway/protopb/gateway.pb.go +++ b/api/submitqueue/gateway/protopb/gateway.pb.go @@ -788,6 +788,318 @@ func (x *ListResponse) GetNextPageToken() string { return "" } +// HistoryByIDRequest selects one retained request history by sqid. +type HistoryByIDRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Globally unique identifier for the request. + Sqid string `protobuf:"bytes,1,opt,name=sqid,proto3" json:"sqid,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HistoryByIDRequest) Reset() { + *x = HistoryByIDRequest{} + mi := &file_gateway_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HistoryByIDRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistoryByIDRequest) ProtoMessage() {} + +func (x *HistoryByIDRequest) ProtoReflect() protoreflect.Message { + mi := &file_gateway_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HistoryByIDRequest.ProtoReflect.Descriptor instead. +func (*HistoryByIDRequest) Descriptor() ([]byte, []int) { + return file_gateway_proto_rawDescGZIP(), []int{13} +} + +func (x *HistoryByIDRequest) GetSqid() string { + if x != nil { + return x.Sqid + } + return "" +} + +// HistoryEvent is one retained append-only request-log event. +type HistoryEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Time the request-log entry was created, in Unix milliseconds. + TimestampMs int64 `protobuf:"varint,1,opt,name=timestamp_ms,json=timestampMs,proto3" json:"timestamp_ms,omitempty"` + // Customer-friendly request status recorded by the event. + Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` + // Error message associated with the event. Empty when absent. + LastError string `protobuf:"bytes,3,opt,name=last_error,json=lastError,proto3" json:"last_error,omitempty"` + // Display and debugging metadata associated with the event. + Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HistoryEvent) Reset() { + *x = HistoryEvent{} + mi := &file_gateway_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HistoryEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistoryEvent) ProtoMessage() {} + +func (x *HistoryEvent) ProtoReflect() protoreflect.Message { + mi := &file_gateway_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HistoryEvent.ProtoReflect.Descriptor instead. +func (*HistoryEvent) Descriptor() ([]byte, []int) { + return file_gateway_proto_rawDescGZIP(), []int{14} +} + +func (x *HistoryEvent) GetTimestampMs() int64 { + if x != nil { + return x.TimestampMs + } + return 0 +} + +func (x *HistoryEvent) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *HistoryEvent) GetLastError() string { + if x != nil { + return x.LastError + } + return "" +} + +func (x *HistoryEvent) GetMetadata() map[string]string { + if x != nil { + return x.Metadata + } + return nil +} + +// HistoryByIDResponse contains all retained events for one request. +type HistoryByIDResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Events ordered by timestamp ascending with a stable storage tie-breaker. + Events []*HistoryEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HistoryByIDResponse) Reset() { + *x = HistoryByIDResponse{} + mi := &file_gateway_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HistoryByIDResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistoryByIDResponse) ProtoMessage() {} + +func (x *HistoryByIDResponse) ProtoReflect() protoreflect.Message { + mi := &file_gateway_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HistoryByIDResponse.ProtoReflect.Descriptor instead. +func (*HistoryByIDResponse) Descriptor() ([]byte, []int) { + return file_gateway_proto_rawDescGZIP(), []int{15} +} + +func (x *HistoryByIDResponse) GetEvents() []*HistoryEvent { + if x != nil { + return x.Events + } + return nil +} + +// HistoryByChangeIDRequest selects retained histories by an exact pinned change URI. +type HistoryByChangeIDRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Exact change URI supplied in a Land request. + ChangeId string `protobuf:"bytes,1,opt,name=change_id,json=changeId,proto3" json:"change_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HistoryByChangeIDRequest) Reset() { + *x = HistoryByChangeIDRequest{} + mi := &file_gateway_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HistoryByChangeIDRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistoryByChangeIDRequest) ProtoMessage() {} + +func (x *HistoryByChangeIDRequest) ProtoReflect() protoreflect.Message { + mi := &file_gateway_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HistoryByChangeIDRequest.ProtoReflect.Descriptor instead. +func (*HistoryByChangeIDRequest) Descriptor() ([]byte, []int) { + return file_gateway_proto_rawDescGZIP(), []int{16} +} + +func (x *HistoryByChangeIDRequest) GetChangeId() string { + if x != nil { + return x.ChangeId + } + return "" +} + +// RequestHistory groups retained events for one request. +type RequestHistory struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Globally unique request identifier. + Sqid string `protobuf:"bytes,1,opt,name=sqid,proto3" json:"sqid,omitempty"` + // Retained events ordered chronologically. + Events []*HistoryEvent `protobuf:"bytes,2,rep,name=events,proto3" json:"events,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RequestHistory) Reset() { + *x = RequestHistory{} + mi := &file_gateway_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RequestHistory) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RequestHistory) ProtoMessage() {} + +func (x *RequestHistory) ProtoReflect() protoreflect.Message { + mi := &file_gateway_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RequestHistory.ProtoReflect.Descriptor instead. +func (*RequestHistory) Descriptor() ([]byte, []int) { + return file_gateway_proto_rawDescGZIP(), []int{17} +} + +func (x *RequestHistory) GetSqid() string { + if x != nil { + return x.Sqid + } + return "" +} + +func (x *RequestHistory) GetEvents() []*HistoryEvent { + if x != nil { + return x.Events + } + return nil +} + +// HistoryByChangeIDResponse contains retained histories ordered by numeric sqid counter ascending. +type HistoryByChangeIDResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Matching request histories. + Histories []*RequestHistory `protobuf:"bytes,1,rep,name=histories,proto3" json:"histories,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *HistoryByChangeIDResponse) Reset() { + *x = HistoryByChangeIDResponse{} + mi := &file_gateway_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *HistoryByChangeIDResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HistoryByChangeIDResponse) ProtoMessage() {} + +func (x *HistoryByChangeIDResponse) ProtoReflect() protoreflect.Message { + mi := &file_gateway_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HistoryByChangeIDResponse.ProtoReflect.Descriptor instead. +func (*HistoryByChangeIDResponse) Descriptor() ([]byte, []int) { + return file_gateway_proto_rawDescGZIP(), []int{18} +} + +func (x *HistoryByChangeIDResponse) GetHistories() []*RequestHistory { + if x != nil { + return x.Histories + } + return nil +} + // Generic error with metadata. Each custom error type should extend this message. type Error struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -799,7 +1111,7 @@ type Error struct { func (x *Error) Reset() { *x = Error{} - mi := &file_gateway_proto_msgTypes[13] + mi := &file_gateway_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -811,7 +1123,7 @@ func (x *Error) String() string { func (*Error) ProtoMessage() {} func (x *Error) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[13] + mi := &file_gateway_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -824,7 +1136,7 @@ func (x *Error) ProtoReflect() protoreflect.Message { // Deprecated: Use Error.ProtoReflect.Descriptor instead. func (*Error) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{13} + return file_gateway_proto_rawDescGZIP(), []int{19} } func (x *Error) GetMessage() string { @@ -847,7 +1159,7 @@ type UnrecognizedQueueError struct { func (x *UnrecognizedQueueError) Reset() { *x = UnrecognizedQueueError{} - mi := &file_gateway_proto_msgTypes[14] + mi := &file_gateway_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -859,7 +1171,7 @@ func (x *UnrecognizedQueueError) String() string { func (*UnrecognizedQueueError) ProtoMessage() {} func (x *UnrecognizedQueueError) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[14] + mi := &file_gateway_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -872,7 +1184,7 @@ func (x *UnrecognizedQueueError) ProtoReflect() protoreflect.Message { // Deprecated: Use UnrecognizedQueueError.ProtoReflect.Descriptor instead. func (*UnrecognizedQueueError) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{14} + return file_gateway_proto_rawDescGZIP(), []int{20} } func (x *UnrecognizedQueueError) GetError() *Error { @@ -904,7 +1216,7 @@ type RequestNotFoundError struct { func (x *RequestNotFoundError) Reset() { *x = RequestNotFoundError{} - mi := &file_gateway_proto_msgTypes[15] + mi := &file_gateway_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -916,7 +1228,7 @@ func (x *RequestNotFoundError) String() string { func (*RequestNotFoundError) ProtoMessage() {} func (x *RequestNotFoundError) ProtoReflect() protoreflect.Message { - mi := &file_gateway_proto_msgTypes[15] + mi := &file_gateway_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -929,7 +1241,7 @@ func (x *RequestNotFoundError) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestNotFoundError.ProtoReflect.Descriptor instead. func (*RequestNotFoundError) Descriptor() ([]byte, []int) { - return file_gateway_proto_rawDescGZIP(), []int{15} + return file_gateway_proto_rawDescGZIP(), []int{21} } func (x *RequestNotFoundError) GetError() *Error { @@ -1005,7 +1317,27 @@ const file_gateway_proto_rawDesc = "" + "page_token\x18\x05 \x01(\tR\tpageToken\"|\n" + "\fListResponse\x12D\n" + "\brequests\x18\x01 \x03(\v2(.uber.submitqueue.gateway.RequestSummaryR\brequests\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"!\n" + + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"(\n" + + "\x12HistoryByIDRequest\x12\x12\n" + + "\x04sqid\x18\x01 \x01(\tR\x04sqid\"\xf7\x01\n" + + "\fHistoryEvent\x12!\n" + + "\ftimestamp_ms\x18\x01 \x01(\x03R\vtimestampMs\x12\x16\n" + + "\x06status\x18\x02 \x01(\tR\x06status\x12\x1d\n" + + "\n" + + "last_error\x18\x03 \x01(\tR\tlastError\x12P\n" + + "\bmetadata\x18\x04 \x03(\v24.uber.submitqueue.gateway.HistoryEvent.MetadataEntryR\bmetadata\x1a;\n" + + "\rMetadataEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"U\n" + + "\x13HistoryByIDResponse\x12>\n" + + "\x06events\x18\x01 \x03(\v2&.uber.submitqueue.gateway.HistoryEventR\x06events\"7\n" + + "\x18HistoryByChangeIDRequest\x12\x1b\n" + + "\tchange_id\x18\x01 \x01(\tR\bchangeId\"d\n" + + "\x0eRequestHistory\x12\x12\n" + + "\x04sqid\x18\x01 \x01(\tR\x04sqid\x12>\n" + + "\x06events\x18\x02 \x03(\v2&.uber.submitqueue.gateway.HistoryEventR\x06events\"c\n" + + "\x19HistoryByChangeIDResponse\x12F\n" + + "\thistories\x18\x01 \x03(\v2(.uber.submitqueue.gateway.RequestHistoryR\thistories\"!\n" + "\x05Error\x12\x18\n" + "\amessage\x18\x01 \x01(\tR\amessage\"e\n" + "\x16UnrecognizedQueueError\x125\n" + @@ -1014,7 +1346,7 @@ const file_gateway_proto_rawDesc = "" + "\x14RequestNotFoundError\x125\n" + "\x05error\x18\x01 \x01(\v2\x1f.uber.submitqueue.gateway.ErrorR\x05error\x12\x12\n" + "\x04sqid\x18\x02 \x01(\tR\x04sqid\x12\x1b\n" + - "\tchange_id\x18\x03 \x01(\tR\bchangeId2\xe6\x04\n" + + "\tchange_id\x18\x03 \x01(\tR\bchangeId2\xd4\x06\n" + "\x12SubmitQueueGateway\x12W\n" + "\x04Ping\x12%.uber.submitqueue.gateway.PingRequest\x1a&.uber.submitqueue.gateway.PingResponse\"\x00\x12W\n" + "\x04Land\x12%.uber.submitqueue.gateway.LandRequest\x1a&.uber.submitqueue.gateway.LandResponse\"\x00\x12]\n" + @@ -1022,7 +1354,9 @@ const file_gateway_proto_rawDesc = "" + "\n" + "StatusByID\x12+.uber.submitqueue.gateway.StatusByIDRequest\x1a,.uber.submitqueue.gateway.StatusByIDResponse\"\x00\x12{\n" + "\x10StatusByChangeID\x121.uber.submitqueue.gateway.StatusByChangeIDRequest\x1a2.uber.submitqueue.gateway.StatusByChangeIDResponse\"\x00\x12W\n" + - "\x04List\x12%.uber.submitqueue.gateway.ListRequest\x1a&.uber.submitqueue.gateway.ListResponse\"\x00Bk\n" + + "\x04List\x12%.uber.submitqueue.gateway.ListRequest\x1a&.uber.submitqueue.gateway.ListResponse\"\x00\x12l\n" + + "\vHistoryByID\x12,.uber.submitqueue.gateway.HistoryByIDRequest\x1a-.uber.submitqueue.gateway.HistoryByIDResponse\"\x00\x12~\n" + + "\x11HistoryByChangeID\x122.uber.submitqueue.gateway.HistoryByChangeIDRequest\x1a3.uber.submitqueue.gateway.HistoryByChangeIDResponse\"\x00Bk\n" + "\x1ccom.uber.submitqueue.gatewayB\fGatewayProtoP\x01Z;github.com/uber/submitqueue/api/submitqueue/gateway/protopbb\x06proto3" var ( @@ -1037,54 +1371,69 @@ func file_gateway_proto_rawDescGZIP() []byte { return file_gateway_proto_rawDescData } -var file_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_gateway_proto_msgTypes = make([]protoimpl.MessageInfo, 24) var file_gateway_proto_goTypes = []any{ - (*PingRequest)(nil), // 0: uber.submitqueue.gateway.PingRequest - (*PingResponse)(nil), // 1: uber.submitqueue.gateway.PingResponse - (*LandRequest)(nil), // 2: uber.submitqueue.gateway.LandRequest - (*LandResponse)(nil), // 3: uber.submitqueue.gateway.LandResponse - (*CancelRequest)(nil), // 4: uber.submitqueue.gateway.CancelRequest - (*CancelResponse)(nil), // 5: uber.submitqueue.gateway.CancelResponse - (*RequestSummary)(nil), // 6: uber.submitqueue.gateway.RequestSummary - (*StatusByIDRequest)(nil), // 7: uber.submitqueue.gateway.StatusByIDRequest - (*StatusByIDResponse)(nil), // 8: uber.submitqueue.gateway.StatusByIDResponse - (*StatusByChangeIDRequest)(nil), // 9: uber.submitqueue.gateway.StatusByChangeIDRequest - (*StatusByChangeIDResponse)(nil), // 10: uber.submitqueue.gateway.StatusByChangeIDResponse - (*ListRequest)(nil), // 11: uber.submitqueue.gateway.ListRequest - (*ListResponse)(nil), // 12: uber.submitqueue.gateway.ListResponse - (*Error)(nil), // 13: uber.submitqueue.gateway.Error - (*UnrecognizedQueueError)(nil), // 14: uber.submitqueue.gateway.UnrecognizedQueueError - (*RequestNotFoundError)(nil), // 15: uber.submitqueue.gateway.RequestNotFoundError - nil, // 16: uber.submitqueue.gateway.RequestSummary.MetadataEntry - (*protopb.Change)(nil), // 17: uber.base.change.Change - (protopb1.Strategy)(0), // 18: uber.base.mergestrategy.Strategy + (*PingRequest)(nil), // 0: uber.submitqueue.gateway.PingRequest + (*PingResponse)(nil), // 1: uber.submitqueue.gateway.PingResponse + (*LandRequest)(nil), // 2: uber.submitqueue.gateway.LandRequest + (*LandResponse)(nil), // 3: uber.submitqueue.gateway.LandResponse + (*CancelRequest)(nil), // 4: uber.submitqueue.gateway.CancelRequest + (*CancelResponse)(nil), // 5: uber.submitqueue.gateway.CancelResponse + (*RequestSummary)(nil), // 6: uber.submitqueue.gateway.RequestSummary + (*StatusByIDRequest)(nil), // 7: uber.submitqueue.gateway.StatusByIDRequest + (*StatusByIDResponse)(nil), // 8: uber.submitqueue.gateway.StatusByIDResponse + (*StatusByChangeIDRequest)(nil), // 9: uber.submitqueue.gateway.StatusByChangeIDRequest + (*StatusByChangeIDResponse)(nil), // 10: uber.submitqueue.gateway.StatusByChangeIDResponse + (*ListRequest)(nil), // 11: uber.submitqueue.gateway.ListRequest + (*ListResponse)(nil), // 12: uber.submitqueue.gateway.ListResponse + (*HistoryByIDRequest)(nil), // 13: uber.submitqueue.gateway.HistoryByIDRequest + (*HistoryEvent)(nil), // 14: uber.submitqueue.gateway.HistoryEvent + (*HistoryByIDResponse)(nil), // 15: uber.submitqueue.gateway.HistoryByIDResponse + (*HistoryByChangeIDRequest)(nil), // 16: uber.submitqueue.gateway.HistoryByChangeIDRequest + (*RequestHistory)(nil), // 17: uber.submitqueue.gateway.RequestHistory + (*HistoryByChangeIDResponse)(nil), // 18: uber.submitqueue.gateway.HistoryByChangeIDResponse + (*Error)(nil), // 19: uber.submitqueue.gateway.Error + (*UnrecognizedQueueError)(nil), // 20: uber.submitqueue.gateway.UnrecognizedQueueError + (*RequestNotFoundError)(nil), // 21: uber.submitqueue.gateway.RequestNotFoundError + nil, // 22: uber.submitqueue.gateway.RequestSummary.MetadataEntry + nil, // 23: uber.submitqueue.gateway.HistoryEvent.MetadataEntry + (*protopb.Change)(nil), // 24: uber.base.change.Change + (protopb1.Strategy)(0), // 25: uber.base.mergestrategy.Strategy } var file_gateway_proto_depIdxs = []int32{ - 17, // 0: uber.submitqueue.gateway.LandRequest.change:type_name -> uber.base.change.Change - 18, // 1: uber.submitqueue.gateway.LandRequest.strategy:type_name -> uber.base.mergestrategy.Strategy - 16, // 2: uber.submitqueue.gateway.RequestSummary.metadata:type_name -> uber.submitqueue.gateway.RequestSummary.MetadataEntry + 24, // 0: uber.submitqueue.gateway.LandRequest.change:type_name -> uber.base.change.Change + 25, // 1: uber.submitqueue.gateway.LandRequest.strategy:type_name -> uber.base.mergestrategy.Strategy + 22, // 2: uber.submitqueue.gateway.RequestSummary.metadata:type_name -> uber.submitqueue.gateway.RequestSummary.MetadataEntry 6, // 3: uber.submitqueue.gateway.StatusByIDResponse.request:type_name -> uber.submitqueue.gateway.RequestSummary 6, // 4: uber.submitqueue.gateway.StatusByChangeIDResponse.requests:type_name -> uber.submitqueue.gateway.RequestSummary 6, // 5: uber.submitqueue.gateway.ListResponse.requests:type_name -> uber.submitqueue.gateway.RequestSummary - 13, // 6: uber.submitqueue.gateway.UnrecognizedQueueError.error:type_name -> uber.submitqueue.gateway.Error - 13, // 7: uber.submitqueue.gateway.RequestNotFoundError.error:type_name -> uber.submitqueue.gateway.Error - 0, // 8: uber.submitqueue.gateway.SubmitQueueGateway.Ping:input_type -> uber.submitqueue.gateway.PingRequest - 2, // 9: uber.submitqueue.gateway.SubmitQueueGateway.Land:input_type -> uber.submitqueue.gateway.LandRequest - 4, // 10: uber.submitqueue.gateway.SubmitQueueGateway.Cancel:input_type -> uber.submitqueue.gateway.CancelRequest - 7, // 11: uber.submitqueue.gateway.SubmitQueueGateway.StatusByID:input_type -> uber.submitqueue.gateway.StatusByIDRequest - 9, // 12: uber.submitqueue.gateway.SubmitQueueGateway.StatusByChangeID:input_type -> uber.submitqueue.gateway.StatusByChangeIDRequest - 11, // 13: uber.submitqueue.gateway.SubmitQueueGateway.List:input_type -> uber.submitqueue.gateway.ListRequest - 1, // 14: uber.submitqueue.gateway.SubmitQueueGateway.Ping:output_type -> uber.submitqueue.gateway.PingResponse - 3, // 15: uber.submitqueue.gateway.SubmitQueueGateway.Land:output_type -> uber.submitqueue.gateway.LandResponse - 5, // 16: uber.submitqueue.gateway.SubmitQueueGateway.Cancel:output_type -> uber.submitqueue.gateway.CancelResponse - 8, // 17: uber.submitqueue.gateway.SubmitQueueGateway.StatusByID:output_type -> uber.submitqueue.gateway.StatusByIDResponse - 10, // 18: uber.submitqueue.gateway.SubmitQueueGateway.StatusByChangeID:output_type -> uber.submitqueue.gateway.StatusByChangeIDResponse - 12, // 19: uber.submitqueue.gateway.SubmitQueueGateway.List:output_type -> uber.submitqueue.gateway.ListResponse - 14, // [14:20] is the sub-list for method output_type - 8, // [8:14] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 23, // 6: uber.submitqueue.gateway.HistoryEvent.metadata:type_name -> uber.submitqueue.gateway.HistoryEvent.MetadataEntry + 14, // 7: uber.submitqueue.gateway.HistoryByIDResponse.events:type_name -> uber.submitqueue.gateway.HistoryEvent + 14, // 8: uber.submitqueue.gateway.RequestHistory.events:type_name -> uber.submitqueue.gateway.HistoryEvent + 17, // 9: uber.submitqueue.gateway.HistoryByChangeIDResponse.histories:type_name -> uber.submitqueue.gateway.RequestHistory + 19, // 10: uber.submitqueue.gateway.UnrecognizedQueueError.error:type_name -> uber.submitqueue.gateway.Error + 19, // 11: uber.submitqueue.gateway.RequestNotFoundError.error:type_name -> uber.submitqueue.gateway.Error + 0, // 12: uber.submitqueue.gateway.SubmitQueueGateway.Ping:input_type -> uber.submitqueue.gateway.PingRequest + 2, // 13: uber.submitqueue.gateway.SubmitQueueGateway.Land:input_type -> uber.submitqueue.gateway.LandRequest + 4, // 14: uber.submitqueue.gateway.SubmitQueueGateway.Cancel:input_type -> uber.submitqueue.gateway.CancelRequest + 7, // 15: uber.submitqueue.gateway.SubmitQueueGateway.StatusByID:input_type -> uber.submitqueue.gateway.StatusByIDRequest + 9, // 16: uber.submitqueue.gateway.SubmitQueueGateway.StatusByChangeID:input_type -> uber.submitqueue.gateway.StatusByChangeIDRequest + 11, // 17: uber.submitqueue.gateway.SubmitQueueGateway.List:input_type -> uber.submitqueue.gateway.ListRequest + 13, // 18: uber.submitqueue.gateway.SubmitQueueGateway.HistoryByID:input_type -> uber.submitqueue.gateway.HistoryByIDRequest + 16, // 19: uber.submitqueue.gateway.SubmitQueueGateway.HistoryByChangeID:input_type -> uber.submitqueue.gateway.HistoryByChangeIDRequest + 1, // 20: uber.submitqueue.gateway.SubmitQueueGateway.Ping:output_type -> uber.submitqueue.gateway.PingResponse + 3, // 21: uber.submitqueue.gateway.SubmitQueueGateway.Land:output_type -> uber.submitqueue.gateway.LandResponse + 5, // 22: uber.submitqueue.gateway.SubmitQueueGateway.Cancel:output_type -> uber.submitqueue.gateway.CancelResponse + 8, // 23: uber.submitqueue.gateway.SubmitQueueGateway.StatusByID:output_type -> uber.submitqueue.gateway.StatusByIDResponse + 10, // 24: uber.submitqueue.gateway.SubmitQueueGateway.StatusByChangeID:output_type -> uber.submitqueue.gateway.StatusByChangeIDResponse + 12, // 25: uber.submitqueue.gateway.SubmitQueueGateway.List:output_type -> uber.submitqueue.gateway.ListResponse + 15, // 26: uber.submitqueue.gateway.SubmitQueueGateway.HistoryByID:output_type -> uber.submitqueue.gateway.HistoryByIDResponse + 18, // 27: uber.submitqueue.gateway.SubmitQueueGateway.HistoryByChangeID:output_type -> uber.submitqueue.gateway.HistoryByChangeIDResponse + 20, // [20:28] is the sub-list for method output_type + 12, // [12:20] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_gateway_proto_init() } @@ -1098,7 +1447,7 @@ func file_gateway_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_gateway_proto_rawDesc), len(file_gateway_proto_rawDesc)), NumEnums: 0, - NumMessages: 17, + NumMessages: 24, NumExtensions: 0, NumServices: 1, }, diff --git a/api/submitqueue/gateway/protopb/gateway.pb.yarpc.go b/api/submitqueue/gateway/protopb/gateway.pb.yarpc.go index 3dd604ec..78f4b899 100644 --- a/api/submitqueue/gateway/protopb/gateway.pb.yarpc.go +++ b/api/submitqueue/gateway/protopb/gateway.pb.yarpc.go @@ -27,6 +27,8 @@ type SubmitQueueGatewayYARPCClient interface { StatusByID(context.Context, *StatusByIDRequest, ...yarpc.CallOption) (*StatusByIDResponse, error) StatusByChangeID(context.Context, *StatusByChangeIDRequest, ...yarpc.CallOption) (*StatusByChangeIDResponse, error) List(context.Context, *ListRequest, ...yarpc.CallOption) (*ListResponse, error) + HistoryByID(context.Context, *HistoryByIDRequest, ...yarpc.CallOption) (*HistoryByIDResponse, error) + HistoryByChangeID(context.Context, *HistoryByChangeIDRequest, ...yarpc.CallOption) (*HistoryByChangeIDResponse, error) } func newSubmitQueueGatewayYARPCClient(clientConfig transport.ClientConfig, anyResolver v2.AnyResolver, options ...v2.ClientOption) SubmitQueueGatewayYARPCClient { @@ -53,6 +55,8 @@ type SubmitQueueGatewayYARPCServer interface { StatusByID(context.Context, *StatusByIDRequest) (*StatusByIDResponse, error) StatusByChangeID(context.Context, *StatusByChangeIDRequest) (*StatusByChangeIDResponse, error) List(context.Context, *ListRequest) (*ListResponse, error) + HistoryByID(context.Context, *HistoryByIDRequest) (*HistoryByIDResponse, error) + HistoryByChangeID(context.Context, *HistoryByChangeIDRequest) (*HistoryByChangeIDResponse, error) } type buildSubmitQueueGatewayYARPCProceduresParams struct { @@ -126,6 +130,26 @@ func buildSubmitQueueGatewayYARPCProcedures(params buildSubmitQueueGatewayYARPCP }, ), }, + { + MethodName: "HistoryByID", + Handler: v2.NewUnaryHandler( + v2.UnaryHandlerParams{ + Handle: handler.HistoryByID, + NewRequest: newSubmitQueueGatewayServiceHistoryByIDYARPCRequest, + AnyResolver: params.AnyResolver, + }, + ), + }, + { + MethodName: "HistoryByChangeID", + Handler: v2.NewUnaryHandler( + v2.UnaryHandlerParams{ + Handle: handler.HistoryByChangeID, + NewRequest: newSubmitQueueGatewayServiceHistoryByChangeIDYARPCRequest, + AnyResolver: params.AnyResolver, + }, + ), + }, }, OnewayHandlerParams: []v2.BuildProceduresOnewayHandlerParams{}, StreamHandlerParams: []v2.BuildProceduresStreamHandlerParams{}, @@ -310,6 +334,30 @@ func (c *_SubmitQueueGatewayYARPCCaller) List(ctx context.Context, request *List return response, err } +func (c *_SubmitQueueGatewayYARPCCaller) HistoryByID(ctx context.Context, request *HistoryByIDRequest, options ...yarpc.CallOption) (*HistoryByIDResponse, error) { + responseMessage, err := c.streamClient.Call(ctx, "HistoryByID", request, newSubmitQueueGatewayServiceHistoryByIDYARPCResponse, options...) + if responseMessage == nil { + return nil, err + } + response, ok := responseMessage.(*HistoryByIDResponse) + if !ok { + return nil, v2.CastError(emptySubmitQueueGatewayServiceHistoryByIDYARPCResponse, responseMessage) + } + return response, err +} + +func (c *_SubmitQueueGatewayYARPCCaller) HistoryByChangeID(ctx context.Context, request *HistoryByChangeIDRequest, options ...yarpc.CallOption) (*HistoryByChangeIDResponse, error) { + responseMessage, err := c.streamClient.Call(ctx, "HistoryByChangeID", request, newSubmitQueueGatewayServiceHistoryByChangeIDYARPCResponse, options...) + if responseMessage == nil { + return nil, err + } + response, ok := responseMessage.(*HistoryByChangeIDResponse) + if !ok { + return nil, v2.CastError(emptySubmitQueueGatewayServiceHistoryByChangeIDYARPCResponse, responseMessage) + } + return response, err +} + type _SubmitQueueGatewayYARPCHandler struct { server SubmitQueueGatewayYARPCServer } @@ -410,6 +458,38 @@ func (h *_SubmitQueueGatewayYARPCHandler) List(ctx context.Context, requestMessa return response, err } +func (h *_SubmitQueueGatewayYARPCHandler) HistoryByID(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { + var request *HistoryByIDRequest + var ok bool + if requestMessage != nil { + request, ok = requestMessage.(*HistoryByIDRequest) + if !ok { + return nil, v2.CastError(emptySubmitQueueGatewayServiceHistoryByIDYARPCRequest, requestMessage) + } + } + response, err := h.server.HistoryByID(ctx, request) + if response == nil { + return nil, err + } + return response, err +} + +func (h *_SubmitQueueGatewayYARPCHandler) HistoryByChangeID(ctx context.Context, requestMessage proto.Message) (proto.Message, error) { + var request *HistoryByChangeIDRequest + var ok bool + if requestMessage != nil { + request, ok = requestMessage.(*HistoryByChangeIDRequest) + if !ok { + return nil, v2.CastError(emptySubmitQueueGatewayServiceHistoryByChangeIDYARPCRequest, requestMessage) + } + } + response, err := h.server.HistoryByChangeID(ctx, request) + if response == nil { + return nil, err + } + return response, err +} + func newSubmitQueueGatewayServicePingYARPCRequest() proto.Message { return &PingRequest{} } @@ -458,82 +538,111 @@ func newSubmitQueueGatewayServiceListYARPCResponse() proto.Message { return &ListResponse{} } +func newSubmitQueueGatewayServiceHistoryByIDYARPCRequest() proto.Message { + return &HistoryByIDRequest{} +} + +func newSubmitQueueGatewayServiceHistoryByIDYARPCResponse() proto.Message { + return &HistoryByIDResponse{} +} + +func newSubmitQueueGatewayServiceHistoryByChangeIDYARPCRequest() proto.Message { + return &HistoryByChangeIDRequest{} +} + +func newSubmitQueueGatewayServiceHistoryByChangeIDYARPCResponse() proto.Message { + return &HistoryByChangeIDResponse{} +} + var ( - emptySubmitQueueGatewayServicePingYARPCRequest = &PingRequest{} - emptySubmitQueueGatewayServicePingYARPCResponse = &PingResponse{} - emptySubmitQueueGatewayServiceLandYARPCRequest = &LandRequest{} - emptySubmitQueueGatewayServiceLandYARPCResponse = &LandResponse{} - emptySubmitQueueGatewayServiceCancelYARPCRequest = &CancelRequest{} - emptySubmitQueueGatewayServiceCancelYARPCResponse = &CancelResponse{} - emptySubmitQueueGatewayServiceStatusByIDYARPCRequest = &StatusByIDRequest{} - emptySubmitQueueGatewayServiceStatusByIDYARPCResponse = &StatusByIDResponse{} - emptySubmitQueueGatewayServiceStatusByChangeIDYARPCRequest = &StatusByChangeIDRequest{} - emptySubmitQueueGatewayServiceStatusByChangeIDYARPCResponse = &StatusByChangeIDResponse{} - emptySubmitQueueGatewayServiceListYARPCRequest = &ListRequest{} - emptySubmitQueueGatewayServiceListYARPCResponse = &ListResponse{} + emptySubmitQueueGatewayServicePingYARPCRequest = &PingRequest{} + emptySubmitQueueGatewayServicePingYARPCResponse = &PingResponse{} + emptySubmitQueueGatewayServiceLandYARPCRequest = &LandRequest{} + emptySubmitQueueGatewayServiceLandYARPCResponse = &LandResponse{} + emptySubmitQueueGatewayServiceCancelYARPCRequest = &CancelRequest{} + emptySubmitQueueGatewayServiceCancelYARPCResponse = &CancelResponse{} + emptySubmitQueueGatewayServiceStatusByIDYARPCRequest = &StatusByIDRequest{} + emptySubmitQueueGatewayServiceStatusByIDYARPCResponse = &StatusByIDResponse{} + emptySubmitQueueGatewayServiceStatusByChangeIDYARPCRequest = &StatusByChangeIDRequest{} + emptySubmitQueueGatewayServiceStatusByChangeIDYARPCResponse = &StatusByChangeIDResponse{} + emptySubmitQueueGatewayServiceListYARPCRequest = &ListRequest{} + emptySubmitQueueGatewayServiceListYARPCResponse = &ListResponse{} + emptySubmitQueueGatewayServiceHistoryByIDYARPCRequest = &HistoryByIDRequest{} + emptySubmitQueueGatewayServiceHistoryByIDYARPCResponse = &HistoryByIDResponse{} + emptySubmitQueueGatewayServiceHistoryByChangeIDYARPCRequest = &HistoryByChangeIDRequest{} + emptySubmitQueueGatewayServiceHistoryByChangeIDYARPCResponse = &HistoryByChangeIDResponse{} ) var yarpcFileDescriptorClosuref1a937782ebbded5 = [][]byte{ // gateway.proto []byte{ - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xef, 0x6e, 0x1b, 0x45, - 0x10, 0x8f, 0xed, 0xd8, 0xb1, 0xc7, 0x4e, 0x08, 0x4b, 0xd5, 0x9e, 0x4c, 0x51, 0x93, 0x15, 0x34, - 0x96, 0xa8, 0xce, 0xc5, 0x40, 0x85, 0x88, 0xf8, 0x50, 0xb7, 0x05, 0x55, 0x22, 0xc5, 0x9c, 0xa9, - 0x40, 0x48, 0xc8, 0xac, 0xed, 0xe9, 0x65, 0x95, 0xdc, 0x9d, 0xb3, 0xbb, 0x17, 0x70, 0x40, 0x7c, - 0xe3, 0x0d, 0x78, 0x23, 0x3e, 0xf1, 0x22, 0x3c, 0x07, 0xda, 0x3f, 0x77, 0x3e, 0x37, 0xb1, 0x13, - 0x04, 0x9f, 0xbc, 0x3b, 0xf3, 0xfb, 0xcd, 0xcc, 0xfe, 0x76, 0x76, 0x7c, 0xb0, 0x1d, 0x32, 0x85, - 0x3f, 0xb1, 0xb9, 0x3f, 0x13, 0x89, 0x4a, 0x88, 0x97, 0x8e, 0x51, 0xf8, 0x32, 0x1d, 0x47, 0x5c, - 0x9d, 0xa5, 0x98, 0xa2, 0xef, 0xfc, 0x6d, 0xca, 0x66, 0xbc, 0x3b, 0x66, 0x12, 0xbb, 0x93, 0x63, - 0x16, 0x87, 0xd8, 0x35, 0x04, 0xb7, 0xb1, 0xec, 0xf6, 0xc3, 0x1c, 0x13, 0xa1, 0x08, 0x51, 0x2a, - 0xc1, 0x14, 0x86, 0x73, 0x07, 0x5d, 0xb2, 0x59, 0x06, 0x3d, 0x80, 0xe6, 0x80, 0xc7, 0x61, 0x80, - 0x67, 0x29, 0x4a, 0x45, 0x3c, 0xd8, 0x8a, 0x50, 0x4a, 0x16, 0xa2, 0x57, 0xda, 0x2b, 0x75, 0x1a, - 0x41, 0xb6, 0xa5, 0xbf, 0x97, 0xa0, 0x65, 0x91, 0x72, 0x96, 0xc4, 0x12, 0x57, 0x43, 0xc9, 0x3e, - 0xb4, 0x24, 0x8a, 0x73, 0x3e, 0xc1, 0x51, 0xcc, 0x22, 0xf4, 0xca, 0xc6, 0xdd, 0x74, 0xb6, 0x17, - 0x2c, 0x42, 0x72, 0x17, 0x1a, 0x8a, 0x47, 0x28, 0x15, 0x8b, 0x66, 0x5e, 0x65, 0xaf, 0xd4, 0xa9, - 0x04, 0x0b, 0x03, 0x69, 0x43, 0xfd, 0x38, 0x91, 0xca, 0x90, 0x37, 0x0d, 0x39, 0xdf, 0xd3, 0x3f, - 0x4a, 0xd0, 0xfc, 0x92, 0xc5, 0xd3, 0xac, 0xe2, 0x5b, 0x50, 0x35, 0x3a, 0xb9, 0x22, 0xec, 0x86, - 0x3c, 0x84, 0x9a, 0x15, 0xc6, 0x24, 0x6f, 0xf6, 0x3c, 0xdf, 0xe8, 0xaa, 0xa5, 0xf1, 0x9d, 0x62, - 0x4f, 0xcc, 0x4f, 0xe0, 0x70, 0xe4, 0x33, 0xa8, 0x67, 0xd2, 0x98, 0x9c, 0x3b, 0xbd, 0xfd, 0x02, - 0x67, 0x59, 0xba, 0xa1, 0x5b, 0x04, 0x39, 0x85, 0x52, 0x68, 0xd9, 0xaa, 0x9c, 0x3a, 0x04, 0x36, - 0xe5, 0x19, 0x9f, 0xba, 0xaa, 0xcc, 0x9a, 0x1e, 0xc2, 0xf6, 0x13, 0x16, 0x4f, 0xf0, 0x34, 0xab, - 0xfd, 0x0a, 0x10, 0xb9, 0x0d, 0x35, 0x81, 0x4c, 0x26, 0xb1, 0x93, 0xcd, 0xed, 0xe8, 0x2e, 0xec, - 0x64, 0x64, 0x9b, 0x82, 0xfe, 0x55, 0x86, 0x1d, 0x17, 0x69, 0x98, 0x46, 0x11, 0x13, 0xf3, 0x2b, - 0x03, 0xe6, 0x02, 0x95, 0x8b, 0x02, 0xdd, 0x83, 0xa6, 0x3d, 0xf8, 0x28, 0x15, 0x5c, 0x7a, 0x95, - 0xbd, 0x4a, 0xa7, 0x11, 0x80, 0x35, 0xbd, 0x14, 0x5c, 0x92, 0x77, 0x61, 0x47, 0xe0, 0x04, 0xf9, - 0x39, 0x4e, 0x47, 0x4c, 0x8d, 0x22, 0x69, 0x54, 0xa9, 0x04, 0xad, 0xcc, 0xfa, 0x58, 0x1d, 0x49, - 0x5d, 0xad, 0x54, 0x4c, 0xa5, 0xd2, 0xab, 0xda, 0x6a, 0xed, 0x8e, 0xbc, 0x03, 0x70, 0xca, 0xa4, - 0x1a, 0xa1, 0x10, 0x89, 0xf0, 0x6a, 0xc6, 0xd7, 0xd0, 0x96, 0x67, 0xda, 0x40, 0x02, 0xa8, 0x47, - 0xa8, 0xd8, 0x94, 0x29, 0xe6, 0x6d, 0xed, 0x55, 0x3a, 0xcd, 0xde, 0x23, 0x7f, 0x55, 0xe3, 0xfb, - 0xcb, 0x67, 0xf4, 0x8f, 0x1c, 0xf1, 0x59, 0xac, 0xc4, 0x3c, 0xc8, 0xe3, 0xb4, 0x0f, 0x61, 0x7b, - 0xc9, 0x45, 0x76, 0xa1, 0x72, 0x82, 0x73, 0xa7, 0x85, 0x5e, 0x6a, 0x29, 0xce, 0xd9, 0xe9, 0x42, - 0x0a, 0xb3, 0xf9, 0xb4, 0xfc, 0x49, 0x89, 0x1e, 0xc0, 0x9b, 0x43, 0x53, 0x79, 0x7f, 0xfe, 0xfc, - 0xe9, 0x9a, 0xeb, 0xa1, 0xdf, 0x01, 0x29, 0x02, 0xdd, 0x6d, 0xf7, 0x61, 0x4b, 0x58, 0x92, 0x01, - 0x37, 0x7b, 0x9d, 0x9b, 0x1e, 0x27, 0xc8, 0x88, 0xf4, 0x11, 0xdc, 0xc9, 0x22, 0xdb, 0xd6, 0x5c, - 0x14, 0xf2, 0x36, 0x34, 0xdc, 0x65, 0xe5, 0xd5, 0xd4, 0xad, 0xe1, 0xf9, 0x94, 0xfe, 0x08, 0xde, - 0x65, 0x9e, 0xab, 0xeb, 0x29, 0xd4, 0x5d, 0x78, 0xe9, 0x95, 0x8c, 0xce, 0x37, 0x2f, 0x2c, 0x67, - 0xd2, 0x3f, 0xf5, 0x93, 0xe3, 0x52, 0xad, 0x7f, 0x72, 0x1f, 0xc1, 0x9d, 0x62, 0xc3, 0x24, 0x62, - 0xc4, 0x5e, 0x29, 0x14, 0xba, 0x73, 0xca, 0xa6, 0x73, 0xde, 0x5a, 0x74, 0xce, 0x57, 0xe2, 0xb1, - 0xf6, 0x1d, 0x49, 0xf2, 0x00, 0x48, 0xce, 0x1a, 0xe3, 0xab, 0x44, 0xa0, 0x26, 0xd8, 0x89, 0xb0, - 0x9b, 0x79, 0xfa, 0xc6, 0x71, 0x24, 0xb5, 0x10, 0x33, 0x16, 0xe2, 0x48, 0xf2, 0x0b, 0x3b, 0x19, - 0xaa, 0x41, 0x5d, 0x1b, 0x86, 0xfc, 0x02, 0x75, 0xcf, 0x19, 0xa7, 0x4a, 0x4e, 0x30, 0x76, 0xfd, - 0x68, 0xe0, 0xdf, 0x68, 0x03, 0xfd, 0x15, 0x5a, 0xf6, 0x10, 0xff, 0xa7, 0x36, 0xe4, 0x3e, 0xbc, - 0x11, 0xe3, 0xcf, 0x6a, 0x54, 0xc8, 0x6c, 0x9b, 0x6b, 0x5b, 0x9b, 0x07, 0x79, 0xf6, 0x7d, 0xa8, - 0xda, 0xd6, 0x5f, 0x3d, 0x61, 0x11, 0x6e, 0xbf, 0x8c, 0x05, 0x4e, 0x92, 0x30, 0xe6, 0x17, 0x38, - 0xfd, 0x5a, 0x17, 0x60, 0x39, 0x1f, 0x43, 0xd5, 0x3e, 0x24, 0xdb, 0x5c, 0xf7, 0x56, 0xd7, 0x69, - 0xf0, 0x81, 0x45, 0x5f, 0xfd, 0xf2, 0xe9, 0x6f, 0x70, 0xcb, 0x9d, 0xe6, 0x45, 0xa2, 0x3e, 0x4f, - 0xd2, 0x78, 0xfa, 0x9f, 0x92, 0x64, 0x8f, 0xa4, 0x5c, 0x18, 0x39, 0x4b, 0xfd, 0x5a, 0x59, 0xee, - 0xd7, 0xde, 0xdf, 0x9b, 0x40, 0x86, 0x26, 0xaa, 0x39, 0xe1, 0x17, 0x36, 0x28, 0xf9, 0x16, 0x36, - 0xf5, 0xdf, 0x0b, 0x79, 0x6f, 0x75, 0xde, 0xc2, 0x1f, 0x55, 0xfb, 0xfe, 0x75, 0x30, 0x37, 0x24, - 0x37, 0x74, 0x60, 0x3d, 0x99, 0xd7, 0x05, 0x2e, 0xfc, 0x9f, 0xac, 0x0b, 0x5c, 0x1c, 0xf0, 0x74, - 0x83, 0xfc, 0x00, 0x35, 0x3b, 0x91, 0xc9, 0xc1, 0x6a, 0xce, 0xd2, 0xc0, 0x6f, 0x77, 0xae, 0x07, - 0xe6, 0xe1, 0x39, 0xc0, 0x62, 0xd2, 0x90, 0xf7, 0x57, 0x33, 0x2f, 0x0d, 0xae, 0xf6, 0x83, 0x9b, - 0x81, 0xf3, 0x54, 0xbf, 0xc0, 0xee, 0xeb, 0x23, 0x84, 0x7c, 0x70, 0x7d, 0x8c, 0xd7, 0xc6, 0x54, - 0xbb, 0xf7, 0x6f, 0x28, 0x4b, 0xf7, 0xc3, 0xa5, 0x5a, 0x7b, 0x3f, 0x8b, 0xe1, 0xb3, 0xf6, 0x7e, - 0x0a, 0xcf, 0x9b, 0x6e, 0xf4, 0x4f, 0xe0, 0xee, 0x24, 0x89, 0x56, 0xc2, 0xfb, 0x2d, 0xd7, 0x7a, - 0x03, 0xfd, 0x21, 0x34, 0x28, 0x7d, 0x7f, 0x18, 0x72, 0x75, 0x9c, 0x8e, 0xfd, 0x49, 0x12, 0x75, - 0x35, 0xa9, 0x5b, 0x20, 0x75, 0xf5, 0x87, 0x55, 0x71, 0xef, 0x82, 0xd8, 0x2f, 0xab, 0xd9, 0x78, - 0x5c, 0x33, 0x8b, 0x0f, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x1e, 0xa8, 0x2b, 0x6f, 0xcf, 0x09, - 0x00, 0x00, + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x5f, 0x6f, 0x1b, 0x45, + 0x10, 0xcf, 0xd9, 0x89, 0x63, 0x8f, 0x9d, 0x90, 0x6e, 0xab, 0xf6, 0x30, 0x45, 0x4d, 0x56, 0x90, + 0x58, 0x22, 0xd8, 0xc5, 0x2d, 0x05, 0x11, 0x81, 0xd4, 0xb4, 0x29, 0x54, 0x22, 0x25, 0x5c, 0x88, + 0x40, 0x48, 0xc8, 0xac, 0xed, 0xa9, 0x73, 0x4a, 0xee, 0xce, 0xd9, 0xdd, 0x0b, 0x38, 0xa0, 0xbe, + 0xf1, 0x0d, 0xf8, 0x46, 0x3c, 0xf1, 0xce, 0x77, 0xe1, 0x15, 0xed, 0x9f, 0x3b, 0xdf, 0x25, 0xb1, + 0xe3, 0x00, 0x7d, 0xf2, 0xed, 0xec, 0xfc, 0x66, 0x66, 0x7f, 0xf3, 0x67, 0xd7, 0xb0, 0x34, 0x60, + 0x12, 0x7f, 0x62, 0xa3, 0xe6, 0x90, 0x47, 0x32, 0x22, 0x6e, 0xdc, 0x45, 0xde, 0x14, 0x71, 0x37, + 0xf0, 0xe5, 0x49, 0x8c, 0x31, 0x36, 0xed, 0x7e, 0x9d, 0xb2, 0xa1, 0xdf, 0xea, 0x32, 0x81, 0xad, + 0xde, 0x21, 0x0b, 0x07, 0xd8, 0xd2, 0x00, 0xbb, 0x30, 0xe8, 0xfa, 0xfd, 0x54, 0x27, 0x40, 0x3e, + 0x40, 0x21, 0x39, 0x93, 0x38, 0x18, 0x59, 0xd5, 0x9c, 0xcc, 0x20, 0xe8, 0x06, 0x54, 0xf7, 0xfc, + 0x70, 0xe0, 0xe1, 0x49, 0x8c, 0x42, 0x12, 0x17, 0x16, 0x03, 0x14, 0x82, 0x0d, 0xd0, 0x75, 0x56, + 0x9d, 0x46, 0xc5, 0x4b, 0x96, 0xf4, 0x37, 0x07, 0x6a, 0x46, 0x53, 0x0c, 0xa3, 0x50, 0xe0, 0x64, + 0x55, 0xb2, 0x06, 0x35, 0x81, 0xfc, 0xd4, 0xef, 0x61, 0x27, 0x64, 0x01, 0xba, 0x05, 0xbd, 0x5d, + 0xb5, 0xb2, 0x17, 0x2c, 0x40, 0x72, 0x17, 0x2a, 0xd2, 0x0f, 0x50, 0x48, 0x16, 0x0c, 0xdd, 0xe2, + 0xaa, 0xd3, 0x28, 0x7a, 0x63, 0x01, 0xa9, 0x43, 0xf9, 0x30, 0x12, 0x52, 0x83, 0xe7, 0x35, 0x38, + 0x5d, 0xd3, 0xdf, 0x1d, 0xa8, 0x7e, 0xc9, 0xc2, 0x7e, 0x12, 0xf1, 0x2d, 0x58, 0xd0, 0x3c, 0xd9, + 0x20, 0xcc, 0x82, 0xdc, 0x87, 0x92, 0x21, 0x46, 0x3b, 0xaf, 0xb6, 0xdd, 0xa6, 0xe6, 0x55, 0x51, + 0xd3, 0xb4, 0x8c, 0x3d, 0xd1, 0x3f, 0x9e, 0xd5, 0x23, 0x9f, 0x42, 0x39, 0xa1, 0x46, 0xfb, 0x5c, + 0x6e, 0xaf, 0x65, 0x30, 0x79, 0xea, 0xf6, 0xed, 0x87, 0x97, 0x42, 0x28, 0x85, 0x9a, 0x89, 0xca, + 0xb2, 0x43, 0x60, 0x5e, 0x9c, 0xf8, 0x7d, 0x1b, 0x95, 0xfe, 0xa6, 0x5b, 0xb0, 0xf4, 0x84, 0x85, + 0x3d, 0x3c, 0x4e, 0x62, 0xbf, 0x44, 0x89, 0xdc, 0x86, 0x12, 0x47, 0x26, 0xa2, 0xd0, 0xd2, 0x66, + 0x57, 0x74, 0x05, 0x96, 0x13, 0xb0, 0x71, 0x41, 0xff, 0x2c, 0xc0, 0xb2, 0xb5, 0xb4, 0x1f, 0x07, + 0x01, 0xe3, 0xa3, 0x4b, 0x0d, 0xa6, 0x04, 0x15, 0xb2, 0x04, 0xdd, 0x83, 0xaa, 0x39, 0x78, 0x27, + 0xe6, 0xbe, 0x70, 0x8b, 0xab, 0xc5, 0x46, 0xc5, 0x03, 0x23, 0x3a, 0xe0, 0xbe, 0x20, 0xef, 0xc0, + 0x32, 0xc7, 0x1e, 0xfa, 0xa7, 0xd8, 0xef, 0x30, 0xd9, 0x09, 0x84, 0x66, 0xa5, 0xe8, 0xd5, 0x12, + 0xe9, 0x63, 0xb9, 0x2b, 0x54, 0xb4, 0x42, 0x32, 0x19, 0x0b, 0x77, 0xc1, 0x44, 0x6b, 0x56, 0xe4, + 0x6d, 0x80, 0x63, 0x26, 0x64, 0x07, 0x39, 0x8f, 0xb8, 0x5b, 0xd2, 0x7b, 0x15, 0x25, 0xd9, 0x51, + 0x02, 0xe2, 0x41, 0x39, 0x40, 0xc9, 0xfa, 0x4c, 0x32, 0x77, 0x71, 0xb5, 0xd8, 0xa8, 0xb6, 0x1f, + 0x35, 0x27, 0x15, 0x7e, 0x33, 0x7f, 0xc6, 0xe6, 0xae, 0x05, 0xee, 0x84, 0x92, 0x8f, 0xbc, 0xd4, + 0x4e, 0x7d, 0x0b, 0x96, 0x72, 0x5b, 0x64, 0x05, 0x8a, 0x47, 0x38, 0xb2, 0x5c, 0xa8, 0x4f, 0x45, + 0xc5, 0x29, 0x3b, 0x1e, 0x53, 0xa1, 0x17, 0x9f, 0x14, 0x3e, 0x76, 0xe8, 0x06, 0xdc, 0xd8, 0xd7, + 0x91, 0x6f, 0x8f, 0x9e, 0x3f, 0x9d, 0x92, 0x1e, 0xfa, 0x1d, 0x90, 0xac, 0xa2, 0xcd, 0xf6, 0x36, + 0x2c, 0x72, 0x03, 0xd2, 0xca, 0xd5, 0x76, 0x63, 0xd6, 0xe3, 0x78, 0x09, 0x90, 0x3e, 0x82, 0x3b, + 0x89, 0x65, 0x53, 0x9a, 0xe3, 0x40, 0xde, 0x82, 0x8a, 0x4d, 0x56, 0x1a, 0x4d, 0xd9, 0x08, 0x9e, + 0xf7, 0xe9, 0x8f, 0xe0, 0x5e, 0xc4, 0xd9, 0xb8, 0x9e, 0x42, 0xd9, 0x9a, 0x17, 0xae, 0xa3, 0x79, + 0x9e, 0x3d, 0xb0, 0x14, 0x49, 0xff, 0x50, 0x2d, 0xe7, 0x0b, 0x39, 0xbd, 0xe5, 0x1e, 0xc2, 0x9d, + 0x6c, 0xc1, 0x44, 0xbc, 0xc3, 0x5e, 0x4a, 0xe4, 0xaa, 0x72, 0x0a, 0xba, 0x72, 0x6e, 0x8e, 0x2b, + 0xe7, 0x2b, 0xfe, 0x58, 0xed, 0xed, 0x0a, 0xb2, 0x09, 0x24, 0x45, 0x75, 0xf1, 0x65, 0xc4, 0x51, + 0x01, 0xcc, 0x44, 0x58, 0x49, 0x76, 0xb6, 0xf5, 0xc6, 0xae, 0x50, 0x44, 0x0c, 0xd9, 0x00, 0x3b, + 0xc2, 0x3f, 0x33, 0x93, 0x61, 0xc1, 0x2b, 0x2b, 0xc1, 0xbe, 0x7f, 0x86, 0xaa, 0xe6, 0xf4, 0xa6, + 0x8c, 0x8e, 0x30, 0xb4, 0xf5, 0xa8, 0xd5, 0xbf, 0x51, 0x02, 0xfa, 0x2b, 0xd4, 0xcc, 0x21, 0xfe, + 0x4f, 0x6e, 0xc8, 0x3a, 0xbc, 0x11, 0xe2, 0xcf, 0xb2, 0x93, 0xf1, 0x6c, 0x8a, 0x6b, 0x49, 0x89, + 0xf7, 0x52, 0xef, 0x0d, 0x20, 0x5f, 0xf8, 0x42, 0x46, 0x7c, 0x74, 0x55, 0x85, 0xfd, 0xed, 0x40, + 0xcd, 0xaa, 0xee, 0x9c, 0x62, 0x28, 0xd5, 0x38, 0x4d, 0x47, 0xa3, 0x22, 0xc7, 0xd1, 0xe4, 0x54, + 0x53, 0x59, 0xae, 0x0d, 0x0b, 0x53, 0xda, 0xb0, 0x78, 0xbe, 0x0d, 0xf7, 0x32, 0x6d, 0x38, 0xaf, + 0x29, 0x78, 0x38, 0x99, 0x82, 0x6c, 0x4c, 0xaf, 0xa7, 0x09, 0x0f, 0xe0, 0x66, 0x8e, 0x23, 0x9b, + 0xa8, 0xcf, 0xa0, 0x84, 0xca, 0x69, 0x92, 0xa6, 0xf5, 0xd9, 0x62, 0xf4, 0x2c, 0x8a, 0x7e, 0x04, + 0x6e, 0x6a, 0xf6, 0x5a, 0x9d, 0xd5, 0x4f, 0xe7, 0xab, 0xc5, 0x5f, 0x3a, 0x5f, 0xc7, 0xe1, 0x15, + 0xfe, 0x55, 0x78, 0x3d, 0x78, 0xf3, 0x92, 0xf0, 0xec, 0xd9, 0x9f, 0x41, 0xe5, 0x50, 0x6f, 0xfa, + 0x38, 0x7b, 0x95, 0x5a, 0x73, 0xde, 0x18, 0x4a, 0xd7, 0x60, 0xc1, 0xa4, 0x7c, 0xf2, 0x05, 0x8f, + 0x70, 0xfb, 0x20, 0xe4, 0xd8, 0x8b, 0x06, 0xa1, 0x7f, 0x86, 0xfd, 0xaf, 0x95, 0x65, 0x83, 0xf9, + 0x10, 0x16, 0x4c, 0x01, 0x99, 0xd9, 0x76, 0x6f, 0x72, 0x00, 0x5a, 0xdf, 0x33, 0xda, 0x97, 0x5f, + 0x3c, 0xf4, 0x15, 0xdc, 0xb2, 0x61, 0xbe, 0x88, 0xe4, 0xb3, 0x28, 0x0e, 0xfb, 0xff, 0xc9, 0x49, + 0x92, 0x91, 0x42, 0x26, 0x23, 0xb9, 0xa4, 0x16, 0xf3, 0x49, 0x6d, 0xff, 0x55, 0x02, 0xb2, 0xaf, + 0xad, 0xea, 0x13, 0x7e, 0x6e, 0x8c, 0x92, 0x6f, 0x61, 0x5e, 0xbd, 0x6e, 0xc8, 0xbb, 0x93, 0xfd, + 0x66, 0xde, 0x49, 0xf5, 0xf5, 0xab, 0xd4, 0xec, 0x1d, 0x3d, 0xa7, 0x0c, 0xab, 0x87, 0xc1, 0x34, + 0xc3, 0x99, 0xe7, 0xcc, 0x34, 0xc3, 0xd9, 0xf7, 0x05, 0x9d, 0x23, 0x3f, 0x40, 0xc9, 0x3c, 0x08, + 0xc8, 0xc6, 0x64, 0x4c, 0xee, 0xbd, 0x51, 0x6f, 0x5c, 0xad, 0x98, 0x9a, 0xf7, 0x01, 0xc6, 0x17, + 0x1d, 0x79, 0x6f, 0x32, 0xf2, 0xc2, 0xbd, 0x59, 0xdf, 0x9c, 0x4d, 0x39, 0x75, 0xf5, 0x0b, 0xac, + 0x9c, 0xbf, 0xc1, 0xc8, 0x07, 0x57, 0xdb, 0x38, 0xd7, 0xcb, 0xf5, 0xf6, 0x75, 0x20, 0xb9, 0xfc, + 0xf8, 0x42, 0x4e, 0xcd, 0xcf, 0xf8, 0xee, 0x9b, 0x9a, 0x9f, 0xcc, 0xed, 0x42, 0xe7, 0xc8, 0x31, + 0x54, 0x33, 0xd3, 0x8c, 0x6c, 0x5e, 0x39, 0x16, 0xb2, 0x14, 0xbe, 0x3f, 0xa3, 0x76, 0xea, 0xed, + 0x15, 0xdc, 0xb8, 0x30, 0x45, 0x48, 0x7b, 0x06, 0x2b, 0xe7, 0x59, 0x7c, 0x70, 0x2d, 0x4c, 0xe2, + 0x7f, 0xfb, 0x08, 0xee, 0xf6, 0xa2, 0x60, 0x22, 0x76, 0xbb, 0x66, 0x1b, 0x6d, 0x4f, 0xfd, 0xeb, + 0xd8, 0x73, 0xbe, 0xdf, 0x1a, 0xf8, 0xf2, 0x30, 0xee, 0x36, 0x7b, 0x51, 0xd0, 0x52, 0xa0, 0x56, + 0x06, 0xd4, 0x52, 0xff, 0x62, 0xb2, 0x6b, 0x6b, 0xc4, 0xfc, 0x8d, 0x19, 0x76, 0xbb, 0x25, 0xfd, + 0xf1, 0xe0, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6e, 0xc5, 0x88, 0x7c, 0x3c, 0x0d, 0x00, 0x00, }, // api/base/change/proto/change.proto []byte{ diff --git a/api/submitqueue/gateway/protopb/gateway_grpc.pb.go b/api/submitqueue/gateway/protopb/gateway_grpc.pb.go index 3c4be4d1..307f55d9 100644 --- a/api/submitqueue/gateway/protopb/gateway_grpc.pb.go +++ b/api/submitqueue/gateway/protopb/gateway_grpc.pb.go @@ -34,12 +34,14 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - SubmitQueueGateway_Ping_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/Ping" - SubmitQueueGateway_Land_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/Land" - SubmitQueueGateway_Cancel_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/Cancel" - SubmitQueueGateway_StatusByID_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/StatusByID" - SubmitQueueGateway_StatusByChangeID_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/StatusByChangeID" - SubmitQueueGateway_List_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/List" + SubmitQueueGateway_Ping_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/Ping" + SubmitQueueGateway_Land_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/Land" + SubmitQueueGateway_Cancel_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/Cancel" + SubmitQueueGateway_StatusByID_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/StatusByID" + SubmitQueueGateway_StatusByChangeID_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/StatusByChangeID" + SubmitQueueGateway_List_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/List" + SubmitQueueGateway_HistoryByID_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/HistoryByID" + SubmitQueueGateway_HistoryByChangeID_FullMethodName = "/uber.submitqueue.gateway.SubmitQueueGateway/HistoryByChangeID" ) // SubmitQueueGatewayClient is the client API for SubmitQueueGateway service. @@ -71,6 +73,10 @@ type SubmitQueueGatewayClient interface { StatusByChangeID(ctx context.Context, in *StatusByChangeIDRequest, opts ...grpc.CallOption) (*StatusByChangeIDResponse, error) // List returns requests received for one queue during a bounded receipt-time range. List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) + // HistoryByID returns every retained request-log event for one request. + HistoryByID(ctx context.Context, in *HistoryByIDRequest, opts ...grpc.CallOption) (*HistoryByIDResponse, error) + // HistoryByChangeID returns retained request histories for an exact pinned change URI. + HistoryByChangeID(ctx context.Context, in *HistoryByChangeIDRequest, opts ...grpc.CallOption) (*HistoryByChangeIDResponse, error) } type submitQueueGatewayClient struct { @@ -141,6 +147,26 @@ func (c *submitQueueGatewayClient) List(ctx context.Context, in *ListRequest, op return out, nil } +func (c *submitQueueGatewayClient) HistoryByID(ctx context.Context, in *HistoryByIDRequest, opts ...grpc.CallOption) (*HistoryByIDResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(HistoryByIDResponse) + err := c.cc.Invoke(ctx, SubmitQueueGateway_HistoryByID_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *submitQueueGatewayClient) HistoryByChangeID(ctx context.Context, in *HistoryByChangeIDRequest, opts ...grpc.CallOption) (*HistoryByChangeIDResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(HistoryByChangeIDResponse) + err := c.cc.Invoke(ctx, SubmitQueueGateway_HistoryByChangeID_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // SubmitQueueGatewayServer is the server API for SubmitQueueGateway service. // All implementations must embed UnimplementedSubmitQueueGatewayServer // for forward compatibility. @@ -170,6 +196,10 @@ type SubmitQueueGatewayServer interface { StatusByChangeID(context.Context, *StatusByChangeIDRequest) (*StatusByChangeIDResponse, error) // List returns requests received for one queue during a bounded receipt-time range. List(context.Context, *ListRequest) (*ListResponse, error) + // HistoryByID returns every retained request-log event for one request. + HistoryByID(context.Context, *HistoryByIDRequest) (*HistoryByIDResponse, error) + // HistoryByChangeID returns retained request histories for an exact pinned change URI. + HistoryByChangeID(context.Context, *HistoryByChangeIDRequest) (*HistoryByChangeIDResponse, error) mustEmbedUnimplementedSubmitQueueGatewayServer() } @@ -198,6 +228,12 @@ func (UnimplementedSubmitQueueGatewayServer) StatusByChangeID(context.Context, * func (UnimplementedSubmitQueueGatewayServer) List(context.Context, *ListRequest) (*ListResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method List not implemented") } +func (UnimplementedSubmitQueueGatewayServer) HistoryByID(context.Context, *HistoryByIDRequest) (*HistoryByIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method HistoryByID not implemented") +} +func (UnimplementedSubmitQueueGatewayServer) HistoryByChangeID(context.Context, *HistoryByChangeIDRequest) (*HistoryByChangeIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method HistoryByChangeID not implemented") +} func (UnimplementedSubmitQueueGatewayServer) mustEmbedUnimplementedSubmitQueueGatewayServer() {} func (UnimplementedSubmitQueueGatewayServer) testEmbeddedByValue() {} @@ -327,6 +363,42 @@ func _SubmitQueueGateway_List_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _SubmitQueueGateway_HistoryByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HistoryByIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SubmitQueueGatewayServer).HistoryByID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SubmitQueueGateway_HistoryByID_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SubmitQueueGatewayServer).HistoryByID(ctx, req.(*HistoryByIDRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SubmitQueueGateway_HistoryByChangeID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HistoryByChangeIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SubmitQueueGatewayServer).HistoryByChangeID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SubmitQueueGateway_HistoryByChangeID_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SubmitQueueGatewayServer).HistoryByChangeID(ctx, req.(*HistoryByChangeIDRequest)) + } + return interceptor(ctx, in, info, handler) +} + // SubmitQueueGateway_ServiceDesc is the grpc.ServiceDesc for SubmitQueueGateway service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -358,6 +430,14 @@ var SubmitQueueGateway_ServiceDesc = grpc.ServiceDesc{ MethodName: "List", Handler: _SubmitQueueGateway_List_Handler, }, + { + MethodName: "HistoryByID", + Handler: _SubmitQueueGateway_HistoryByID_Handler, + }, + { + MethodName: "HistoryByChangeID", + Handler: _SubmitQueueGateway_HistoryByChangeID_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "gateway.proto", diff --git a/service/README.md b/service/README.md index 35e9fe08..efa908a2 100644 --- a/service/README.md +++ b/service/README.md @@ -12,7 +12,7 @@ Each domain has its own subdirectory with a dedicated README: | Service | Port | Domain | RPCs | Backing stores | |---------|------|--------|------|----------------| -| **SubmitQueue Gateway** | 8081 | `submitqueue` | `Ping`, `Land` | MySQL app + queue | +| **SubmitQueue Gateway** | 8081 | `submitqueue` | `Ping`, `Land`, `Cancel`, `StatusByID`, `StatusByChangeID`, `List`, `HistoryByID`, `HistoryByChangeID` | MySQL app + queue | | **SubmitQueue Orchestrator** | 8082 | `submitqueue` | `Ping` (+ consumes 9 pipeline topics) | MySQL app + queue | | **Stovepipe** | 8083 | `stovepipe` | `Ping`, `Ingest` (+ consumes the process topic) | MySQL storage + queue | | **Runway** | 8086 | `runway` | `Ping` (+ consumes merge-conflict-check & merge topics) | MySQL queue | @@ -145,6 +145,14 @@ grpcurl -plaintext localhost:8081 describe uber.submitqueue.gateway.SubmitQueueG |--------|-------------| | `Ping` | Health check, returns service name and timestamp | | `Land` | Submit a land request for code changes | +| `Cancel` | Record and publish a best-effort cancellation intent | +| `StatusByID` | Read one authoritative current request summary by sqid | +| `StatusByChangeID` | Read authoritative current summaries for one exact pinned change URI | +| `List` | Page through queue-scoped request receipt history | +| `HistoryByID` | Read every retained request-log event for one sqid | +| `HistoryByChangeID` | Read retained request histories for one exact pinned change URI | + +The gateway owns request receipts, current-status projections, and the append-only request log. `List` is ordered by immutable gateway receipt time. The queue projection may briefly lag the authoritative summaries returned by the Status methods while materialization converges. ### Orchestrator Service diff --git a/service/submitqueue/gateway/server/main.go b/service/submitqueue/gateway/server/main.go index e807b06b..aa2a9002 100644 --- a/service/submitqueue/gateway/server/main.go +++ b/service/submitqueue/gateway/server/main.go @@ -52,11 +52,12 @@ import ( // GatewayServer wraps the controller and implements the gRPC service interface type GatewayServer struct { pb.UnimplementedSubmitQueueGatewayServer - pingController *controller.PingController - landController *controller.LandController - cancelController *controller.CancelController - statusController *controller.StatusController - listController *controller.ListController + pingController *controller.PingController + landController *controller.LandController + cancelController *controller.CancelController + statusController *controller.StatusController + listController *controller.ListController + historyController *controller.HistoryController } // Ping delegates to the controller @@ -114,6 +115,24 @@ func (s *GatewayServer) List(ctx context.Context, req *pb.ListRequest) (*pb.List return mapper.ListResultToProto(result), nil } +// HistoryByID maps the wire request to an entity, delegates to the controller, and maps the result back to the wire response. +func (s *GatewayServer) HistoryByID(ctx context.Context, req *pb.HistoryByIDRequest) (*pb.HistoryByIDResponse, error) { + events, err := s.historyController.HistoryByID(ctx, mapper.ProtoToHistoryByIDRequest(req)) + if err != nil { + return nil, err + } + return &pb.HistoryByIDResponse{Events: mapper.HistoryEventsToProto(events)}, nil +} + +// HistoryByChangeID maps the wire request to an entity, delegates to the controller, and maps the result back to the wire response. +func (s *GatewayServer) HistoryByChangeID(ctx context.Context, req *pb.HistoryByChangeIDRequest) (*pb.HistoryByChangeIDResponse, error) { + histories, err := s.historyController.HistoryByChangeID(ctx, mapper.ProtoToHistoryByChangeIDRequest(req)) + if err != nil { + return nil, err + } + return &pb.HistoryByChangeIDResponse{Histories: mapper.RequestHistoriesToProto(histories)}, nil +} + func gatewayStatusError(err error) error { switch { case controller.IsRequestNotFound(err): @@ -278,15 +297,13 @@ func run() error { }, )) - // Initialize storage from the shared app database connection. The land - // controller writes to this store directly; cancel and status use its - // request stores. The log consumer registered below persists request log - // entries published by the orchestrator. + // Initialize gateway-owned storage from the shared app database connection. + // Land creates receipt projections, Status and List read materialized views, + // History reads retained logs, and every log writer uses the materializer. store, err := mysqlstorage.NewStorage(appDB, scope.SubScope("storage")) if err != nil { return fmt.Errorf("failed to create storage: %w", err) } - // Load queue configurations from YAML. Path is required so the gateway // can reject requests for unknown queues at the edge. queueConfigPath := os.Getenv("QUEUE_CONFIG_PATH") @@ -309,12 +326,19 @@ func run() error { store.GetRequestURIStore(), ) listController := controller.NewListController(logger.Sugar(), scope, store.GetRequestQueueSummaryStore(), queueConfigs) + historyController := controller.NewHistoryController( + logger.Sugar(), + scope, + store.GetRequestLogStore(), + store.GetRequestURIStore(), + ) gatewayServer := &GatewayServer{ - pingController: pingController, - landController: landController, - cancelController: cancelController, - statusController: statusController, - listController: listController, + pingController: pingController, + landController: landController, + cancelController: cancelController, + statusController: statusController, + listController: listController, + historyController: historyController, } pb.RegisterSubmitQueueGatewayServer(grpcServer, gatewayServer) diff --git a/service/submitqueue/gateway/server/mapper/BUILD.bazel b/service/submitqueue/gateway/server/mapper/BUILD.bazel index 2a29abbb..28d4673d 100644 --- a/service/submitqueue/gateway/server/mapper/BUILD.bazel +++ b/service/submitqueue/gateway/server/mapper/BUILD.bazel @@ -4,6 +4,7 @@ go_library( name = "go_default_library", srcs = [ "cancel.go", + "history.go", "land.go", "list.go", "status.go", @@ -23,6 +24,7 @@ go_test( name = "go_default_test", srcs = [ "cancel_test.go", + "history_test.go", "land_test.go", "list_test.go", "status_test.go", diff --git a/service/submitqueue/gateway/server/mapper/history.go b/service/submitqueue/gateway/server/mapper/history.go new file mode 100644 index 00000000..0c84f05e --- /dev/null +++ b/service/submitqueue/gateway/server/mapper/history.go @@ -0,0 +1,56 @@ +// Copyright (c) 2025 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mapper + +import ( + pb "github.com/uber/submitqueue/api/submitqueue/gateway/protopb" + "github.com/uber/submitqueue/submitqueue/entity" +) + +// ProtoToHistoryByIDRequest maps the wire request to the entity request the controller operates on. +func ProtoToHistoryByIDRequest(req *pb.HistoryByIDRequest) entity.HistoryByIDRequest { + return entity.HistoryByIDRequest{ID: req.GetSqid()} +} + +// ProtoToHistoryByChangeIDRequest maps the wire request to the entity request the controller operates on. +func ProtoToHistoryByChangeIDRequest(req *pb.HistoryByChangeIDRequest) entity.HistoryByChangeIDRequest { + return entity.HistoryByChangeIDRequest{ChangeID: req.GetChangeId()} +} + +// HistoryEventsToProto maps retained request-log events to wire history events. +func HistoryEventsToProto(logs []entity.RequestLog) []*pb.HistoryEvent { + events := make([]*pb.HistoryEvent, len(logs)) + for i, log := range logs { + events[i] = &pb.HistoryEvent{ + TimestampMs: log.TimestampMs, + Status: string(log.Status), + LastError: log.LastError, + Metadata: cloneStringMap(log.Metadata), + } + } + return events +} + +// RequestHistoriesToProto maps grouped retained histories to the wire representation. +func RequestHistoriesToProto(histories []entity.RequestHistory) []*pb.RequestHistory { + result := make([]*pb.RequestHistory, len(histories)) + for i, history := range histories { + result[i] = &pb.RequestHistory{ + Sqid: history.RequestID, + Events: HistoryEventsToProto(history.Events), + } + } + return result +} diff --git a/service/submitqueue/gateway/server/mapper/history_test.go b/service/submitqueue/gateway/server/mapper/history_test.go new file mode 100644 index 00000000..64371c72 --- /dev/null +++ b/service/submitqueue/gateway/server/mapper/history_test.go @@ -0,0 +1,60 @@ +// Copyright (c) 2025 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mapper + +import ( + "testing" + + "github.com/stretchr/testify/assert" + pb "github.com/uber/submitqueue/api/submitqueue/gateway/protopb" + "github.com/uber/submitqueue/submitqueue/entity" +) + +func TestProtoToHistoryRequests(t *testing.T) { + assert.Equal(t, + entity.HistoryByIDRequest{ID: "q/1"}, + ProtoToHistoryByIDRequest(&pb.HistoryByIDRequest{Sqid: "q/1"}), + ) + assert.Equal(t, + entity.HistoryByChangeIDRequest{ChangeID: "uri"}, + ProtoToHistoryByChangeIDRequest(&pb.HistoryByChangeIDRequest{ChangeId: "uri"}), + ) +} + +func TestHistoryEventsToProto(t *testing.T) { + events := HistoryEventsToProto([]entity.RequestLog{{ + TimestampMs: 10, + Status: entity.RequestStatusError, + LastError: "failed", + Metadata: map[string]string{"step": "build"}, + }}) + + assert.Equal(t, &pb.HistoryEvent{ + TimestampMs: 10, + Status: string(entity.RequestStatusError), + LastError: "failed", + Metadata: map[string]string{"step": "build"}, + }, events[0]) +} + +func TestRequestHistoriesToProto(t *testing.T) { + histories := RequestHistoriesToProto([]entity.RequestHistory{{ + RequestID: "q/1", + Events: []entity.RequestLog{{TimestampMs: 10, Status: entity.RequestStatusAccepted}}, + }}) + + assert.Equal(t, "q/1", histories[0].Sqid) + assert.Equal(t, string(entity.RequestStatusAccepted), histories[0].Events[0].Status) +} diff --git a/submitqueue/entity/BUILD.bazel b/submitqueue/entity/BUILD.bazel index 977ac357..02c098a2 100644 --- a/submitqueue/entity/BUILD.bazel +++ b/submitqueue/entity/BUILD.bazel @@ -11,6 +11,7 @@ go_library( "change_provider.go", "change_record.go", "conflict.go", + "history.go", "land.go", "list.go", "merge_result.go", diff --git a/submitqueue/entity/history.go b/submitqueue/entity/history.go new file mode 100644 index 00000000..3fd47f79 --- /dev/null +++ b/submitqueue/entity/history.go @@ -0,0 +1,35 @@ +// Copyright (c) 2025 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package entity + +// HistoryByIDRequest identifies one retained request history by request ID. +type HistoryByIDRequest struct { + // ID is the globally unique identifier of the request. + ID string +} + +// HistoryByChangeIDRequest identifies retained histories by an exact pinned change URI. +type HistoryByChangeIDRequest struct { + // ChangeID is the exact change URI supplied in a Land request. + ChangeID string +} + +// RequestHistory groups retained events for one request. +type RequestHistory struct { + // RequestID is the globally unique identifier of the request. + RequestID string + // Events are retained request-log events ordered chronologically. + Events []RequestLog +} diff --git a/submitqueue/gateway/README.md b/submitqueue/gateway/README.md index 5903f009..b09937c1 100644 --- a/submitqueue/gateway/README.md +++ b/submitqueue/gateway/README.md @@ -1,25 +1,23 @@ # SubmitQueue Gateway -The gateway is the RPC entry point to SubmitQueue. It accepts `Land`, `Cancel`, -`Status`, and `Ping` calls, validates them at the edge, and hands work off to the -orchestrator pipeline asynchronously via the message queue. +The gateway is the RPC entry point to SubmitQueue. It accepts `Land`, `Cancel`, `StatusByID`, `StatusByChangeID`, `List`, `HistoryByID`, `HistoryByChangeID`, and `Ping` calls. It validates edge-owned input and hands asynchronous work to the orchestrator through the message queue. -## Request log ownership +## Request receipts and current status + +`Land` creates gateway-owned receipt projections before publishing the request: + +- An authoritative request summary keyed by sqid. +- One exact change-URI mapping per submitted URI. +- A queue-ordered receipt projection used by `List`. -The gateway is the **sole owner of the request log** — the only service that -both writes and reads it. No other service persists or reads request log -entries: +`StatusByID` and `StatusByChangeID` read authoritative summaries. `List` reads the queue projection and may briefly lag authoritative Status while eventual repair converges. + +## Request log ownership -- For statuses it produces synchronously (`accepted` on `Land`, `cancelling` on - `Cancel`), the gateway writes directly to storage so the entry is visible the - moment the RPC returns. -- For statuses produced downstream, the orchestrator only *publishes* entries to - the `log` topic via `submitqueue/core/request.PublishLog`. The gateway runs a - consumer that drains the `log` topic and persists each entry to storage. +The gateway is the sole owner of the request log and the only service that persists or reads it. -Reads are likewise gateway-only: the `Status` and `Cancel` RPCs read the request -log directly from storage. The orchestrator only *publishes* log entries and -never touches the request log store. +- For statuses produced synchronously by the gateway, such as `accepted` on `Land` and `cancelling` on `Cancel`, the gateway persists the event through the shared request-log materializer before returning or publishing. +- For statuses produced downstream, the orchestrator publishes entries to the `log` topic through `submitqueue/core/request.PublishLog`. The gateway consumes that topic and persists each entry through the same materializer. +- `HistoryByID` and `HistoryByChangeID` read retained request-log rows directly. -This keeps a single service responsible for the request log while letting the -orchestrator remain free of storage writes for it. +The materializer appends every audit event, selects the current authoritative winner, and repairs the queue projection. The orchestrator never reads or writes the request-log store directly. diff --git a/submitqueue/gateway/controller/BUILD.bazel b/submitqueue/gateway/controller/BUILD.bazel index dba8fe2f..53e6bed5 100644 --- a/submitqueue/gateway/controller/BUILD.bazel +++ b/submitqueue/gateway/controller/BUILD.bazel @@ -4,6 +4,7 @@ go_library( name = "go_default_library", srcs = [ "cancel.go", + "history.go", "land.go", "list.go", "ping.go", @@ -33,6 +34,7 @@ go_test( name = "go_default_test", srcs = [ "cancel_test.go", + "history_test.go", "land_test.go", "list_test.go", "ping_test.go", diff --git a/submitqueue/gateway/controller/history.go b/submitqueue/gateway/controller/history.go new file mode 100644 index 00000000..50bfb544 --- /dev/null +++ b/submitqueue/gateway/controller/history.go @@ -0,0 +1,158 @@ +// Copyright (c) 2025 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controller + +import ( + "context" + "fmt" + "sort" + "strconv" + "strings" + "time" + + "github.com/uber-go/tally" + "github.com/uber/submitqueue/platform/errs" + "github.com/uber/submitqueue/submitqueue/entity" + "github.com/uber/submitqueue/submitqueue/extension/storage" + "go.uber.org/zap" +) + +// HistoryController handles retained request-log history lookups. +type HistoryController struct { + logger *zap.SugaredLogger + metricsScope tally.Scope + requestLogStore storage.RequestLogStore + requestURIStore storage.RequestURIStore +} + +// NewHistoryController creates a gateway history controller. +func NewHistoryController(logger *zap.SugaredLogger, scope tally.Scope, requestLogStore storage.RequestLogStore, requestURIStore storage.RequestURIStore) *HistoryController { + return &HistoryController{ + logger: logger, + metricsScope: scope.SubScope("history_controller"), + requestLogStore: requestLogStore, + requestURIStore: requestURIStore, + } +} + +// HistoryByID returns every retained request-log event for one sqid. +func (c *HistoryController) HistoryByID(ctx context.Context, req entity.HistoryByIDRequest) ([]entity.RequestLog, error) { + start := time.Now() + defer func() { + c.metricsScope.Timer("by_id_latency").Record(time.Since(start)) + }() + c.metricsScope.Counter("by_id_count").Inc(1) + + if err := validateStoredIdentifier("sqid", req.ID); err != nil { + return nil, fmt.Errorf("HistoryByID invalid request: %w", err) + } + + logs, err := c.requestLogStore.List(ctx, req.ID) + if err != nil { + if storage.IsNotFound(err) { + return nil, errs.NewUserError(&RequestNotFoundError{Sqid: req.ID}) + } + return nil, fmt.Errorf("HistoryByID failed to list request logs sqid=%s: %w", req.ID, err) + } + + c.logger.Debugw("request history retrieved", + "sqid", req.ID, + "event_count", len(logs), + ) + return logs, nil +} + +// HistoryByChangeID returns retained histories for an exact pinned change URI. +func (c *HistoryController) HistoryByChangeID(ctx context.Context, req entity.HistoryByChangeIDRequest) ([]entity.RequestHistory, error) { + start := time.Now() + defer func() { + c.metricsScope.Timer("by_change_id_latency").Record(time.Since(start)) + }() + c.metricsScope.Counter("by_change_id_count").Inc(1) + + if err := validateStoredIdentifier("change ID", req.ChangeID); err != nil { + return nil, fmt.Errorf("HistoryByChangeID invalid request: %w", err) + } + + mappings, err := c.requestURIStore.ListByURI(ctx, req.ChangeID, maxChangeRequestResults+1) + if err != nil { + return nil, fmt.Errorf("HistoryByChangeID failed to list request mappings change_id=%s: %w", req.ChangeID, err) + } + if len(mappings) == 0 { + return nil, errs.NewUserError(&RequestNotFoundError{ChangeID: req.ChangeID}) + } + if len(mappings) > maxChangeRequestResults { + return nil, errs.NewUserError(&TooManyChangeRequestsError{ChangeID: req.ChangeID, Limit: maxChangeRequestResults}) + } + + histories := make([]requestHistoryWithCounter, 0, len(mappings)) + for _, mapping := range mappings { + logs, err := c.requestLogStore.List(ctx, mapping.RequestID) + if err != nil { + if storage.IsNotFound(err) { + continue + } + return nil, fmt.Errorf("HistoryByChangeID failed to list request logs change_id=%s sqid=%s: %w", req.ChangeID, mapping.RequestID, err) + } + counter, err := sqidCounter(mapping.RequestID) + if err != nil { + return nil, &InternalConsistencyError{Message: fmt.Sprintf("invalid mapped sqid %q: %v", mapping.RequestID, err)} + } + histories = append(histories, requestHistoryWithCounter{ + counter: counter, + history: entity.RequestHistory{ + RequestID: mapping.RequestID, + Events: append([]entity.RequestLog{}, logs...), + }, + }) + } + if len(histories) == 0 { + return nil, errs.NewUserError(&RequestNotFoundError{ChangeID: req.ChangeID}) + } + + sort.Slice(histories, func(i, j int) bool { + if histories[i].counter != histories[j].counter { + return histories[i].counter < histories[j].counter + } + return histories[i].history.RequestID < histories[j].history.RequestID + }) + + result := make([]entity.RequestHistory, len(histories)) + for i, history := range histories { + result[i] = history.history + } + c.logger.Debugw("request histories retrieved", + "change_id", req.ChangeID, + "request_count", len(result), + ) + return result, nil +} + +type requestHistoryWithCounter struct { + counter int64 + history entity.RequestHistory +} + +func sqidCounter(sqid string) (int64, error) { + separator := strings.LastIndexByte(sqid, '/') + if separator < 0 || separator == len(sqid)-1 { + return 0, fmt.Errorf("missing numeric counter") + } + counter, err := strconv.ParseInt(sqid[separator+1:], 10, 64) + if err != nil || counter <= 0 { + return 0, fmt.Errorf("invalid numeric counter") + } + return counter, nil +} diff --git a/submitqueue/gateway/controller/history_test.go b/submitqueue/gateway/controller/history_test.go new file mode 100644 index 00000000..fef96907 --- /dev/null +++ b/submitqueue/gateway/controller/history_test.go @@ -0,0 +1,209 @@ +// Copyright (c) 2025 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controller + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/uber-go/tally" + "github.com/uber/submitqueue/platform/errs" + "github.com/uber/submitqueue/submitqueue/entity" + "github.com/uber/submitqueue/submitqueue/extension/storage" + storagemock "github.com/uber/submitqueue/submitqueue/extension/storage/mock" + "go.uber.org/mock/gomock" + "go.uber.org/zap" +) + +func TestHistoryByID(t *testing.T) { + ctrl := gomock.NewController(t) + logStore := storagemock.NewMockRequestLogStore(ctrl) + uriStore := storagemock.NewMockRequestURIStore(ctrl) + logStore.EXPECT().List(gomock.Any(), "queue/1").Return([]entity.RequestLog{ + {RequestID: "queue/1", TimestampMs: 10, Status: entity.RequestStatusAccepted, Metadata: map[string]string{}}, + {RequestID: "queue/1", TimestampMs: 20, Status: entity.RequestStatusStarted, LastError: "retry", Metadata: map[string]string{"attempt": "1"}}, + {RequestID: "queue/1", TimestampMs: 20, Status: entity.RequestStatusStarted, LastError: "retry", Metadata: map[string]string{"attempt": "1"}}, + }, nil) + + controller := NewHistoryController(zap.NewNop().Sugar(), tally.NoopScope, logStore, uriStore) + events, err := controller.HistoryByID(context.Background(), entity.HistoryByIDRequest{ID: "queue/1"}) + + require.NoError(t, err) + require.Len(t, events, 3) + assert.Equal(t, []entity.RequestStatus{entity.RequestStatusAccepted, entity.RequestStatusStarted, entity.RequestStatusStarted}, []entity.RequestStatus{events[0].Status, events[1].Status, events[2].Status}) + assert.Equal(t, int64(20), events[1].TimestampMs) + assert.Equal(t, "retry", events[1].LastError) + assert.Equal(t, map[string]string{"attempt": "1"}, events[1].Metadata) +} + +func TestHistoryByChangeID(t *testing.T) { + ctrl := gomock.NewController(t) + logStore := storagemock.NewMockRequestLogStore(ctrl) + uriStore := storagemock.NewMockRequestURIStore(ctrl) + uriStore.EXPECT().ListByURI(gomock.Any(), "uri", 101).Return([]entity.RequestURI{ + {ChangeURI: "uri", RequestID: "queue/10"}, + {ChangeURI: "uri", RequestID: "b/2"}, + {ChangeURI: "uri", RequestID: "missing/3"}, + {ChangeURI: "uri", RequestID: "queue/1"}, + {ChangeURI: "uri", RequestID: "a/2"}, + }, nil) + logStore.EXPECT().List(gomock.Any(), "queue/10").Return([]entity.RequestLog{{RequestID: "queue/10", TimestampMs: 10, Status: entity.RequestStatusLanded}}, nil) + logStore.EXPECT().List(gomock.Any(), "b/2").Return([]entity.RequestLog{{RequestID: "b/2", TimestampMs: 2, Status: entity.RequestStatusStarted}}, nil) + logStore.EXPECT().List(gomock.Any(), "missing/3").Return(nil, storage.ErrNotFound) + logStore.EXPECT().List(gomock.Any(), "queue/1").Return([]entity.RequestLog{{RequestID: "queue/1", TimestampMs: 1, Status: entity.RequestStatusAccepted}}, nil) + logStore.EXPECT().List(gomock.Any(), "a/2").Return([]entity.RequestLog{{RequestID: "a/2", TimestampMs: 2, Status: entity.RequestStatusError}}, nil) + + controller := NewHistoryController(zap.NewNop().Sugar(), tally.NoopScope, logStore, uriStore) + histories, err := controller.HistoryByChangeID(context.Background(), entity.HistoryByChangeIDRequest{ChangeID: "uri"}) + + require.NoError(t, err) + require.Len(t, histories, 4) + assert.Equal(t, []string{"queue/1", "a/2", "b/2", "queue/10"}, []string{ + histories[0].RequestID, + histories[1].RequestID, + histories[2].RequestID, + histories[3].RequestID, + }) +} + +func TestHistoryErrors(t *testing.T) { + backendErr := fmt.Errorf("backend down") + tests := []struct { + name string + call func(*HistoryController) error + setup func(*storagemock.MockRequestLogStore, *storagemock.MockRequestURIStore) + wantInvalid bool + wantNotFound bool + wantTooMany bool + wantInternal bool + wantUser bool + }{ + { + name: "empty sqid", + call: func(c *HistoryController) error { + _, err := c.HistoryByID(context.Background(), entity.HistoryByIDRequest{}) + return err + }, + wantInvalid: true, + wantUser: true, + }, + { + name: "empty change ID", + call: func(c *HistoryController) error { + _, err := c.HistoryByChangeID(context.Background(), entity.HistoryByChangeIDRequest{}) + return err + }, + wantInvalid: true, + wantUser: true, + }, + { + name: "sqid not found", + setup: func(logStore *storagemock.MockRequestLogStore, _ *storagemock.MockRequestURIStore) { + logStore.EXPECT().List(gomock.Any(), "missing/1").Return(nil, storage.ErrNotFound) + }, + call: func(c *HistoryController) error { + _, err := c.HistoryByID(context.Background(), entity.HistoryByIDRequest{ID: "missing/1"}) + return err + }, + wantNotFound: true, + wantUser: true, + }, + { + name: "sqid storage failure", + setup: func(logStore *storagemock.MockRequestLogStore, _ *storagemock.MockRequestURIStore) { + logStore.EXPECT().List(gomock.Any(), "queue/1").Return(nil, backendErr) + }, + call: func(c *HistoryController) error { + _, err := c.HistoryByID(context.Background(), entity.HistoryByIDRequest{ID: "queue/1"}) + return err + }, + }, + { + name: "change ID not found", + setup: func(_ *storagemock.MockRequestLogStore, uriStore *storagemock.MockRequestURIStore) { + uriStore.EXPECT().ListByURI(gomock.Any(), "uri", 101).Return(nil, nil) + }, + call: func(c *HistoryController) error { + _, err := c.HistoryByChangeID(context.Background(), entity.HistoryByChangeIDRequest{ChangeID: "uri"}) + return err + }, + wantNotFound: true, + wantUser: true, + }, + { + name: "too many change matches", + setup: func(_ *storagemock.MockRequestLogStore, uriStore *storagemock.MockRequestURIStore) { + uriStore.EXPECT().ListByURI(gomock.Any(), "uri", 101).Return(make([]entity.RequestURI, 101), nil) + }, + call: func(c *HistoryController) error { + _, err := c.HistoryByChangeID(context.Background(), entity.HistoryByChangeIDRequest{ChangeID: "uri"}) + return err + }, + wantTooMany: true, + wantUser: true, + }, + { + name: "all mapped logs absent", + setup: func(logStore *storagemock.MockRequestLogStore, uriStore *storagemock.MockRequestURIStore) { + uriStore.EXPECT().ListByURI(gomock.Any(), "uri", 101).Return([]entity.RequestURI{{RequestID: "queue/1"}}, nil) + logStore.EXPECT().List(gomock.Any(), "queue/1").Return(nil, storage.ErrNotFound) + }, + call: func(c *HistoryController) error { + _, err := c.HistoryByChangeID(context.Background(), entity.HistoryByChangeIDRequest{ChangeID: "uri"}) + return err + }, + wantNotFound: true, + wantUser: true, + }, + { + name: "mapped sqid malformed", + setup: func(logStore *storagemock.MockRequestLogStore, uriStore *storagemock.MockRequestURIStore) { + uriStore.EXPECT().ListByURI(gomock.Any(), "uri", 101).Return([]entity.RequestURI{{RequestID: "malformed"}}, nil) + logStore.EXPECT().List(gomock.Any(), "malformed").Return([]entity.RequestLog{{RequestID: "malformed"}}, nil) + }, + call: func(c *HistoryController) error { + _, err := c.HistoryByChangeID(context.Background(), entity.HistoryByChangeIDRequest{ChangeID: "uri"}) + return err + }, + wantInternal: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + logStore := storagemock.NewMockRequestLogStore(ctrl) + uriStore := storagemock.NewMockRequestURIStore(ctrl) + if tt.setup != nil { + tt.setup(logStore, uriStore) + } + controller := NewHistoryController(zap.NewNop().Sugar(), tally.NoopScope, logStore, uriStore) + + err := tt.call(controller) + + require.Error(t, err) + if tt.wantInvalid { + assert.True(t, IsInvalidRequest(err)) + } + assert.Equal(t, tt.wantNotFound, IsRequestNotFound(err)) + assert.Equal(t, tt.wantTooMany, IsTooManyChangeRequests(err)) + assert.Equal(t, tt.wantInternal, IsInternalConsistency(err)) + assert.Equal(t, tt.wantUser, errs.IsUserError(err)) + }) + } +} diff --git a/test/e2e/submitqueue/harness_test.go b/test/e2e/submitqueue/harness_test.go index 5dc3b32c..601ea69c 100644 --- a/test/e2e/submitqueue/harness_test.go +++ b/test/e2e/submitqueue/harness_test.go @@ -19,8 +19,7 @@ package e2e_test // observe outcomes two ways: // // - black-box, by polling the StatusByID RPC to a target/terminal status; and -// - white-box, by reading the request_log timeline (RequestLogStore.List on -// mysql-app) to assert the ordered stage progression. +// - black-box, by reading the ordered stage progression through HistoryByID. // // Convergence is bounded by require.Eventually (persistTimeout / // persistPollInterval) rather than time.Sleep: the pipeline consumers run inside @@ -100,23 +99,21 @@ func (s *E2EIntegrationSuite) awaitTerminal(sqid string) entity.RequestStatus { return last } -// timeline returns the ordered status history from the request_log (the audit -// trail persisted by the gateway log consumer on mysql-app). These are the -// customer-facing RequestStatus values — the only ordered history in the system -// (the internal RequestState is point-in-time, see terminalState). +// timeline returns the ordered customer-facing status history through +// HistoryByID. func (s *E2EIntegrationSuite) timeline(sqid string) []entity.RequestStatus { t := s.T() - logs, err := s.requestLog.List(s.ctx, sqid) - require.NoError(t, err, "failed to list request_log for %s", sqid) - statuses := make([]entity.RequestStatus, len(logs)) - for i, l := range logs { - statuses[i] = l.Status + resp, err := s.gatewayClient.HistoryByID(s.ctx, &gatewaypb.HistoryByIDRequest{Sqid: sqid}) + require.NoError(t, err, "HistoryByID failed for %s", sqid) + statuses := make([]entity.RequestStatus, len(resp.Events)) + for i, event := range resp.Events { + statuses[i] = entity.RequestStatus(event.Status) } return statuses } // assertStatusesInOrder asserts that want appears as an ordered subsequence of -// the request_log status timeline. It tolerates intermediate statuses (so it is +// the HistoryByID status timeline. It tolerates intermediate statuses (so it is // not a change-detector), asserting only the relative order of the statuses that // matter. func (s *E2EIntegrationSuite) assertStatusesInOrder(sqid string, want ...entity.RequestStatus) { @@ -129,7 +126,7 @@ func (s *E2EIntegrationSuite) assertStatusesInOrder(sqid string, want ...entity. } } assert.Equalf(t, len(want), matched, - "request_log for %s should contain %v as an ordered subsequence; got %v", + "HistoryByID for %s should contain %v as an ordered subsequence; got %v", sqid, want, got) } diff --git a/test/e2e/submitqueue/suite_test.go b/test/e2e/submitqueue/suite_test.go index 19d27bc3..79b059bf 100644 --- a/test/e2e/submitqueue/suite_test.go +++ b/test/e2e/submitqueue/suite_test.go @@ -51,22 +51,19 @@ type E2EIntegrationSuite struct { stack *testutil.ComposeStack gatewayClient gatewaypb.SubmitQueueGatewayClient orchestratorClient orchestratorpb.SubmitQueueOrchestratorClient - db *sql.DB // App database - queueDB *sql.DB // Queue database - requestLog storage.RequestLogStore // White-box view of the request_log status timeline (app DB) - requestStore storage.RequestStore // White-box view of the internal RequestState (app DB) + db *sql.DB // App database + queueDB *sql.DB // Queue database + requestStore storage.RequestStore // White-box view of the internal RequestState (app DB) } func TestE2EIntegration(t *testing.T) { suite.Run(t, new(E2EIntegrationSuite)) } -// The gateway log consumer runs inside the gateway-service container, so this -// suite can only observe persistence black-box through the Status RPC — there is -// no in-process channel/HookSignal to wait on across the container boundary. A -// bounded poll is therefore the deterministic-enough analog: persistTimeout is a -// safety net (a failure here means something is genuinely stuck, not a timing -// race), and persistPollInterval bounds how often we re-query. +// The gateway log consumer runs inside the gateway-service container, so there +// is no in-process signal to wait on across the container boundary. A bounded +// StatusByID poll is therefore the deterministic-enough analog: persistTimeout +// is a safety net, and persistPollInterval bounds how often we re-query. const ( persistTimeout = 30 * time.Second persistPollInterval = 500 * time.Millisecond @@ -111,9 +108,7 @@ func (s *E2EIntegrationSuite) SetupSuite() { s.log.Logf("Schemas applied successfully") - // White-box handles on the app DB: the request_log audit trail (ordered - // status history) and the operating store (point-in-time RequestState). - s.requestLog = storagemysql.NewRequestLogStore(s.db, tally.NoopScope) + // White-box handle on the operating store for point-in-time RequestState. s.requestStore = storagemysql.NewRequestStore(s.db, tally.NoopScope) // Connect to Gateway gRPC service @@ -181,15 +176,15 @@ func (s *E2EIntegrationSuite) TestPingOrchestrator() { // pipeline to terminal success on the fully-hermetic e2e-test-queue (no // conflicts, fake build succeeds, noop runway signals SUCCEEDED for both the // merge-conflict check and the merge). It asserts three views: the black-box -// terminal Status, the ordered request_log status history, and the internal -// RequestState in the operating store. +// terminal Status, the public HistoryByID timeline, and the internal RequestState +// in the operating store. // // This also exercises the request-log ownership invariant end-to-end: the // orchestrator only *publishes* log entries to the log topic (it never writes // the request log itself), and the gateway's log consumer drains that topic and // persists them. Every status below except the synchronous "accepted" reaches // storage only via that cross-service publish→consume→persist path, so its -// presence in the timeline proves the path works. +// presence in HistoryByID proves the path works. func (s *E2EIntegrationSuite) TestLand_HappyPath_ReachesLanded() { sqid := s.land("e2e-test-queue", "github://github.example.com/uber/e2e-service/pull/123/abcdef0123456789abcdef0123456789abcdef01") s.log.Logf("Land (happy path) succeeded: sqid=%s; waiting for landed", sqid) @@ -197,12 +192,11 @@ func (s *E2EIntegrationSuite) TestLand_HappyPath_ReachesLanded() { // Black-box: the customer-facing status reaches landed. s.awaitStatus(sqid, entity.RequestStatusLanded) - // White-box (status history): the request_log is the only ordered trail. All - // status entries for a request share its request_id partition on the log - // topic (ordered delivery) and the terminal "landed" is published last, so - // once "landed" is observed the earlier statuses are already persisted. This - // is a tolerant ordered-subsequence match — display statuses the pipeline - // does not emit (e.g. validating, speculating, building) are omitted. + // Black-box history: all status entries for a request share its request_id + // partition on the log topic, and the terminal "landed" is published last. + // Once "landed" is observed, HistoryByID must expose the earlier statuses. + // This is a tolerant ordered-subsequence match because the pipeline does not + // emit every possible display status. s.assertStatusesInOrder(sqid, entity.RequestStatusAccepted, entity.RequestStatusStarted, @@ -218,6 +212,82 @@ func (s *E2EIntegrationSuite) TestLand_HappyPath_ReachesLanded() { "operating store should show request %s in terminal state landed", sqid) } +// TestReadAPIs validates all five request read endpoints against receipts +// created through the public Land API. +func (s *E2EIntegrationSuite) TestReadAPIs() { + t := s.T() + const ( + queue = "e2e-test-queue" + changeID = "github://uber/e2e-read-apis/pull/456/abcdef0123456789abcdef0123456789abcdef01" + ) + firstSqid := s.land(queue, changeID) + secondSqid := s.land(queue, changeID) + s.awaitStatus(firstSqid, entity.RequestStatusLanded) + s.awaitStatus(secondSqid, entity.RequestStatusError) + + firstStatus, err := s.gatewayClient.StatusByID(s.ctx, &gatewaypb.StatusByIDRequest{Sqid: firstSqid}) + require.NoError(t, err) + require.NotNil(t, firstStatus.Request) + assert.Equal(t, firstSqid, firstStatus.Request.Sqid) + assert.Equal(t, queue, firstStatus.Request.Queue) + assert.Equal(t, []string{changeID}, firstStatus.Request.ChangeUris) + + secondStatus, err := s.gatewayClient.StatusByID(s.ctx, &gatewaypb.StatusByIDRequest{Sqid: secondSqid}) + require.NoError(t, err) + require.NotNil(t, secondStatus.Request) + assert.Contains(t, secondStatus.Request.LastError, firstSqid) + + statusesByChange, err := s.gatewayClient.StatusByChangeID(s.ctx, &gatewaypb.StatusByChangeIDRequest{ChangeId: changeID}) + require.NoError(t, err) + require.Len(t, statusesByChange.Requests, 2) + expectedNewestFirst := []string{firstSqid, secondSqid} + if secondStatus.Request.ReceivedAtMs > firstStatus.Request.ReceivedAtMs || + (secondStatus.Request.ReceivedAtMs == firstStatus.Request.ReceivedAtMs && secondSqid > firstSqid) { + expectedNewestFirst[0], expectedNewestFirst[1] = expectedNewestFirst[1], expectedNewestFirst[0] + } + assert.Equal(t, expectedNewestFirst, []string{statusesByChange.Requests[0].Sqid, statusesByChange.Requests[1].Sqid}) + + receivedAtOrAfterMs := min(firstStatus.Request.ReceivedAtMs, secondStatus.Request.ReceivedAtMs) + receivedBeforeMs := max(firstStatus.Request.ReceivedAtMs, secondStatus.Request.ReceivedAtMs) + 1 + var listedSqids []string + var pageToken string + for { + listResponse, listErr := s.gatewayClient.List(s.ctx, &gatewaypb.ListRequest{ + Queue: queue, + ReceivedAtOrAfterMs: receivedAtOrAfterMs, + ReceivedBeforeMs: receivedBeforeMs, + PageSize: 1, + PageToken: pageToken, + }) + require.NoError(t, listErr) + for _, request := range listResponse.Requests { + if request.Sqid == firstSqid || request.Sqid == secondSqid { + listedSqids = append(listedSqids, request.Sqid) + } + } + pageToken = listResponse.NextPageToken + if pageToken == "" { + break + } + } + assert.Equal(t, expectedNewestFirst, listedSqids) + + historyByID, err := s.gatewayClient.HistoryByID(s.ctx, &gatewaypb.HistoryByIDRequest{Sqid: firstSqid}) + require.NoError(t, err) + require.NotEmpty(t, historyByID.Events) + assert.Equal(t, string(entity.RequestStatusAccepted), historyByID.Events[0].Status) + + historyByChange, err := s.gatewayClient.HistoryByChangeID(s.ctx, &gatewaypb.HistoryByChangeIDRequest{ChangeId: changeID}) + require.NoError(t, err) + require.Len(t, historyByChange.Histories, 2) + assert.Equal(t, []string{firstSqid, secondSqid}, []string{historyByChange.Histories[0].Sqid, historyByChange.Histories[1].Sqid}) + require.NotEmpty(t, historyByChange.Histories[0].Events) + require.NotEmpty(t, historyByChange.Histories[1].Events) + secondEvents := historyByChange.Histories[1].Events + assert.Equal(t, string(entity.RequestStatusError), secondEvents[len(secondEvents)-1].Status) + assert.Equal(t, secondStatus.Request.LastError, secondEvents[len(secondEvents)-1].LastError) +} + // TestCancelRequest_InvalidSqid verifies the gateway rejects an empty sqid // synchronously before publishing anything to the cancel queue. func (s *E2EIntegrationSuite) TestCancelRequest_InvalidSqid() { @@ -252,9 +322,8 @@ func (s *E2EIntegrationSuite) TestCancel_RecordsIntent() { _, err := s.gatewayClient.Cancel(s.ctx, &gatewaypb.CancelRequest{Sqid: sqid, Reason: "e2e cancel test"}) require.NoError(t, err, "Cancel failed") - // The gateway writes "accepted" (on Land) and "cancelling" (on Cancel) - // synchronously to the same store, so both are present the moment Cancel - // returns — no polling needed. + // The gateway writes "accepted" on Land and "cancelling" on Cancel + // synchronously, so HistoryByID exposes both when Cancel returns. s.assertStatusesInOrder(sqid, entity.RequestStatusAccepted, entity.RequestStatusCancelling, diff --git a/test/integration/submitqueue/gateway/suite_test.go b/test/integration/submitqueue/gateway/suite_test.go index 618d28ef..6f7ca403 100644 --- a/test/integration/submitqueue/gateway/suite_test.go +++ b/test/integration/submitqueue/gateway/suite_test.go @@ -236,6 +236,10 @@ func (s *GatewayIntegrationSuite) TestReadAPIErrorCodes() { require.Error(t, err) assert.Equal(t, codes.ResourceExhausted, status.Code(err)) + _, err = s.client.HistoryByChangeID(s.ctx, &pb.HistoryByChangeIDRequest{ChangeId: overflowChangeID}) + require.Error(t, err) + assert.Equal(t, codes.ResourceExhausted, status.Code(err)) + const inconsistentChangeID = "uri/read-api-inconsistent" require.NoError(t, store.GetRequestURIStore().Create(s.ctx, entity.RequestURI{ ChangeURI: inconsistentChangeID,