diff --git a/api/stovepipe/proto/stovepipe.proto b/api/stovepipe/proto/stovepipe.proto index 63ac7d882..5acb2b6e4 100644 --- a/api/stovepipe/proto/stovepipe.proto +++ b/api/stovepipe/proto/stovepipe.proto @@ -196,6 +196,8 @@ message ProjectValidation { oneof result { double breakage_degree = 2; } + // Provider-defined project classification; empty means unknown. + string project_type = 3; } // GetProjectStatusByIDResponse contains the selected validation's current projection. diff --git a/api/stovepipe/protopb/stovepipe.pb.go b/api/stovepipe/protopb/stovepipe.pb.go index 22e99895d..b86449c4a 100644 --- a/api/stovepipe/protopb/stovepipe.pb.go +++ b/api/stovepipe/protopb/stovepipe.pb.go @@ -1230,7 +1230,9 @@ type ProjectValidation struct { // Types that are valid to be assigned to Result: // // *ProjectValidation_BreakageDegree - Result isProjectValidation_Result `protobuf_oneof:"result"` + Result isProjectValidation_Result `protobuf_oneof:"result"` + // Provider-defined project classification; empty means unknown. + ProjectType string `protobuf:"bytes,3,opt,name=project_type,json=projectType,proto3" json:"project_type,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1288,6 +1290,13 @@ func (x *ProjectValidation) GetBreakageDegree() float64 { return 0 } +func (x *ProjectValidation) GetProjectType() string { + if x != nil { + return x.ProjectType + } + return "" +} + type isProjectValidation_Result interface { isProjectValidation_Result() } @@ -2391,10 +2400,11 @@ const file_stovepipe_proto_rawDesc = "" + "\bprojects\x18\x03 \x03(\tR\bprojects\x12\x1b\n" + "\tpage_size\x18\x04 \x01(\x05R\bpageSize\x12\x1d\n" + "\n" + - "page_token\x18\x05 \x01(\tR\tpageToken\"b\n" + + "page_token\x18\x05 \x01(\tR\tpageToken\"\x85\x01\n" + "\x11ProjectValidation\x12\x18\n" + "\aproject\x18\x01 \x01(\tR\aproject\x12)\n" + - "\x0fbreakage_degree\x18\x02 \x01(\x01H\x00R\x0ebreakageDegreeB\b\n" + + "\x0fbreakage_degree\x18\x02 \x01(\x01H\x00R\x0ebreakageDegree\x12!\n" + + "\fproject_type\x18\x03 \x01(\tR\vprojectTypeB\b\n" + "\x06result\"\xd8\x03\n" + "\x1cGetProjectStatusByIDResponse\x12\x1d\n" + "\n" + diff --git a/api/stovepipe/protopb/stovepipe.pb.yarpc.go b/api/stovepipe/protopb/stovepipe.pb.yarpc.go index e63086717..e67b9806e 100644 --- a/api/stovepipe/protopb/stovepipe.pb.yarpc.go +++ b/api/stovepipe/protopb/stovepipe.pb.yarpc.go @@ -627,118 +627,119 @@ var yarpcFileDescriptorClosurefabdb6b3c0b09022 = [][]byte{ // stovepipe.proto []byte{ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x19, 0xcb, 0x6e, 0x1b, 0xd7, - 0x55, 0x43, 0x4a, 0x14, 0x79, 0x44, 0x51, 0xca, 0xb5, 0x4c, 0xd3, 0x8c, 0x9d, 0xba, 0x93, 0xa6, + 0x55, 0x43, 0x4a, 0x14, 0x79, 0x28, 0x51, 0xca, 0xb5, 0x4c, 0xd3, 0x8c, 0x9d, 0xba, 0x93, 0xa6, 0x51, 0xdc, 0x58, 0xaa, 0x95, 0x3e, 0xec, 0xa2, 0x28, 0x22, 0x5a, 0xb2, 0x44, 0xc4, 0x92, 0xe5, 0x91, 0xd5, 0x00, 0xc9, 0x62, 0x70, 0x39, 0x73, 0x44, 0x4d, 0xcc, 0x79, 0x78, 0xee, 0x1d, 0xb5, - 0xca, 0xa2, 0x40, 0x03, 0x74, 0x55, 0xa0, 0x40, 0x7f, 0xa3, 0xdd, 0xf7, 0x13, 0xfa, 0x03, 0xdd, + 0xca, 0xa2, 0x40, 0x03, 0x64, 0x55, 0xa0, 0x40, 0x7f, 0xa3, 0xdd, 0xf7, 0x13, 0xfa, 0x03, 0xdd, 0xb4, 0xdb, 0x02, 0xfd, 0x82, 0x2e, 0xb2, 0x2d, 0xee, 0x63, 0x86, 0xc3, 0x87, 0x28, 0xd2, 0x6a, - 0x80, 0x16, 0xc8, 0x8e, 0xf7, 0xbc, 0xef, 0x79, 0xde, 0x33, 0x84, 0x15, 0xc6, 0xc3, 0x73, 0x8c, + 0x80, 0x16, 0xe8, 0x8e, 0xf7, 0xbc, 0xef, 0x79, 0xde, 0x33, 0x84, 0x15, 0xc6, 0xc3, 0x73, 0x8c, 0xbc, 0x08, 0x37, 0xa2, 0x38, 0xe4, 0x21, 0x69, 0x26, 0x1d, 0x8c, 0x37, 0x58, 0xd2, 0xf1, 0x3d, - 0xfe, 0x3a, 0xc1, 0x04, 0x37, 0x32, 0x0a, 0xf3, 0x7d, 0x58, 0x3a, 0xf2, 0x82, 0xae, 0x85, 0xaf, - 0x13, 0x64, 0x9c, 0x34, 0x60, 0xd1, 0x47, 0xc6, 0x68, 0x17, 0x1b, 0xc6, 0x3d, 0x63, 0xbd, 0x62, - 0xa5, 0x47, 0xf3, 0x77, 0x06, 0x54, 0x15, 0x25, 0x8b, 0xc2, 0x80, 0xe1, 0xe5, 0xa4, 0xe4, 0xbb, - 0x50, 0x65, 0x18, 0x9f, 0x7b, 0x0e, 0xda, 0x01, 0xf5, 0xb1, 0x51, 0x90, 0xe8, 0x25, 0x0d, 0x3b, - 0xa4, 0x3e, 0x92, 0x3b, 0x50, 0xe1, 0x9e, 0x8f, 0x8c, 0x53, 0x3f, 0x6a, 0x14, 0xef, 0x19, 0xeb, - 0x45, 0xab, 0x0f, 0x20, 0x4d, 0x28, 0x9f, 0x85, 0x8c, 0x4b, 0xe6, 0x79, 0xc9, 0x9c, 0x9d, 0xcd, - 0x18, 0x96, 0xdb, 0x41, 0x17, 0x19, 0x4f, 0x4d, 0x5e, 0x83, 0x05, 0x79, 0x29, 0x6d, 0x85, 0x3a, - 0x90, 0x4f, 0x00, 0x30, 0xa0, 0x9d, 0x1e, 0xfa, 0x18, 0x70, 0x69, 0x41, 0x6d, 0xeb, 0x07, 0x1b, - 0x97, 0x3b, 0x62, 0xe3, 0x85, 0x38, 0xef, 0x66, 0x2c, 0x56, 0x8e, 0xdd, 0x64, 0x50, 0x4b, 0x75, - 0xea, 0xcb, 0xd7, 0xa0, 0xe0, 0xb9, 0x5a, 0x63, 0xc1, 0x73, 0xc9, 0x8b, 0x11, 0x75, 0x4b, 0x5b, - 0x0f, 0x67, 0x50, 0x77, 0xcc, 0x29, 0x4f, 0xd8, 0x80, 0xd2, 0x63, 0xb8, 0xb3, 0x87, 0xe9, 0x2d, - 0xf7, 0x3d, 0xc6, 0xc3, 0xf8, 0xa2, 0x75, 0xd1, 0xde, 0x99, 0x7c, 0xef, 0xbb, 0x00, 0xb1, 0x22, - 0xb0, 0x3d, 0x57, 0x7b, 0xbe, 0xa2, 0x21, 0x6d, 0xd7, 0xdc, 0x83, 0xbb, 0x63, 0x84, 0x9e, 0x58, - 0xed, 0xc9, 0x52, 0x57, 0xa1, 0x98, 0xc4, 0x9e, 0x16, 0x27, 0x7e, 0x9a, 0x5f, 0x15, 0xa0, 0xaa, - 0xf9, 0x77, 0xcf, 0x31, 0xe0, 0xe4, 0x36, 0x94, 0x51, 0xfc, 0xb0, 0x33, 0xbf, 0x2c, 0xca, 0x73, - 0xdb, 0x15, 0xf9, 0x90, 0xc5, 0xd6, 0xf6, 0x99, 0x14, 0x53, 0xb4, 0x96, 0x32, 0xd8, 0x01, 0x23, - 0xef, 0xc1, 0x72, 0x6a, 0x36, 0xe3, 0x94, 0xa3, 0xcc, 0x89, 0xca, 0xfe, 0x9c, 0x55, 0xd5, 0x60, - 0xe1, 0x20, 0x24, 0x75, 0x58, 0x90, 0x42, 0x55, 0x56, 0xec, 0xcf, 0x59, 0xea, 0x48, 0xde, 0x83, - 0x5a, 0x98, 0x70, 0x27, 0xf4, 0xd1, 0x8e, 0x91, 0xb2, 0x30, 0x68, 0x2c, 0x48, 0x13, 0x96, 0x35, - 0xd4, 0x92, 0x40, 0xf2, 0x31, 0xcc, 0xbb, 0x94, 0xd3, 0x46, 0x49, 0xc6, 0xe7, 0xc3, 0x49, 0xf1, - 0xc9, 0xdf, 0x6d, 0x87, 0x72, 0x6a, 0x49, 0xce, 0x56, 0x15, 0x20, 0x74, 0x9c, 0x24, 0x8e, 0x31, - 0x70, 0xd0, 0xfc, 0xf7, 0x3c, 0xac, 0x0e, 0x13, 0x92, 0xcf, 0x86, 0xaf, 0x62, 0x48, 0x6d, 0x1f, + 0xfe, 0x3a, 0xc1, 0x04, 0x37, 0x32, 0x0a, 0xf3, 0x7d, 0xa8, 0x1e, 0x79, 0x41, 0xd7, 0xc2, 0xd7, + 0x09, 0x32, 0x4e, 0x1a, 0xb0, 0xe8, 0x23, 0x63, 0xb4, 0x8b, 0x0d, 0xe3, 0x9e, 0xb1, 0x5e, 0xb1, + 0xd2, 0xa3, 0xf9, 0xb5, 0x01, 0x4b, 0x8a, 0x92, 0x45, 0x61, 0xc0, 0xf0, 0x72, 0x52, 0xf2, 0x5d, + 0x58, 0x62, 0x18, 0x9f, 0x7b, 0x0e, 0xda, 0x01, 0xf5, 0xb1, 0x51, 0x90, 0xe8, 0xaa, 0x86, 0x1d, + 0x52, 0x1f, 0xc9, 0x1d, 0xa8, 0x70, 0xcf, 0x47, 0xc6, 0xa9, 0x1f, 0x35, 0x8a, 0xf7, 0x8c, 0xf5, + 0xa2, 0xd5, 0x07, 0x90, 0x26, 0x94, 0xcf, 0x42, 0xc6, 0x25, 0xf3, 0xbc, 0x64, 0xce, 0xce, 0x66, + 0x0c, 0xcb, 0xed, 0xa0, 0x8b, 0x8c, 0xa7, 0x26, 0xaf, 0xc1, 0x82, 0xbc, 0x94, 0xb6, 0x42, 0x1d, + 0xc8, 0x27, 0x00, 0x18, 0xd0, 0x4e, 0x0f, 0x7d, 0x0c, 0xb8, 0xb4, 0xa0, 0xb6, 0xf5, 0x83, 0x8d, + 0xcb, 0x1d, 0xb1, 0xf1, 0x42, 0x9c, 0x77, 0x33, 0x16, 0x2b, 0xc7, 0x6e, 0x32, 0xa8, 0xa5, 0x3a, + 0xf5, 0xe5, 0x6b, 0x50, 0xf0, 0x5c, 0xad, 0xb1, 0xe0, 0xb9, 0xe4, 0xc5, 0x88, 0xba, 0xea, 0xd6, + 0xc3, 0x19, 0xd4, 0x1d, 0x73, 0xca, 0x13, 0x36, 0xa0, 0xf4, 0x18, 0xee, 0xec, 0x61, 0x7a, 0xcb, + 0x7d, 0x8f, 0xf1, 0x30, 0xbe, 0x68, 0x5d, 0xb4, 0x77, 0x26, 0xdf, 0xfb, 0x2e, 0x40, 0xac, 0x08, + 0x6c, 0xcf, 0xd5, 0x9e, 0xaf, 0x68, 0x48, 0xdb, 0x35, 0xf7, 0xe0, 0xee, 0x18, 0xa1, 0x27, 0x56, + 0x7b, 0xb2, 0xd4, 0x55, 0x28, 0x26, 0xb1, 0xa7, 0xc5, 0x89, 0x9f, 0xe6, 0x57, 0x05, 0x58, 0xd2, + 0xfc, 0xbb, 0xe7, 0x18, 0x70, 0x72, 0x1b, 0xca, 0x28, 0x7e, 0xd8, 0x99, 0x5f, 0x16, 0xe5, 0xb9, + 0xed, 0x8a, 0x7c, 0xc8, 0x62, 0x6b, 0xfb, 0x4c, 0x8a, 0x29, 0x5a, 0xd5, 0x0c, 0x76, 0xc0, 0xc8, + 0x7b, 0xb0, 0x9c, 0x9a, 0xcd, 0x38, 0xe5, 0x28, 0x73, 0xa2, 0xb2, 0x3f, 0x67, 0x2d, 0x69, 0xb0, + 0x70, 0x10, 0x92, 0x3a, 0x2c, 0x48, 0xa1, 0x2a, 0x2b, 0xf6, 0xe7, 0x2c, 0x75, 0x24, 0xef, 0x41, + 0x2d, 0x4c, 0xb8, 0x13, 0xfa, 0x68, 0xc7, 0x48, 0x59, 0x18, 0x34, 0x16, 0xa4, 0x09, 0xcb, 0x1a, + 0x6a, 0x49, 0x20, 0xf9, 0x18, 0xe6, 0x5d, 0xca, 0x69, 0xa3, 0x24, 0xe3, 0xf3, 0xe1, 0xa4, 0xf8, + 0xe4, 0xef, 0xb6, 0x43, 0x39, 0xb5, 0x24, 0x67, 0x6b, 0x09, 0x20, 0x74, 0x9c, 0x24, 0x8e, 0x31, + 0x70, 0xd0, 0xfc, 0xd7, 0x3c, 0xac, 0x0e, 0x13, 0x92, 0xcf, 0x86, 0xaf, 0x62, 0x48, 0x6d, 0x1f, 0x4d, 0xa1, 0xcd, 0xca, 0xdd, 0x55, 0xc8, 0x1a, 0xb9, 0xff, 0xa7, 0xb0, 0xd2, 0x49, 0xbc, 0x9e, 0x6b, 0xf3, 0xd8, 0xeb, 0x76, 0x31, 0x46, 0x57, 0xe7, 0xda, 0x34, 0x77, 0x69, 0x09, 0x4e, 0x2d, 0xb6, 0x26, 0xc5, 0xbc, 0x4c, 0xa5, 0x90, 0x13, 0x50, 0x10, 0xfb, 0xd4, 0x0b, 0x3c, 0x76, 0x86, 0xae, 0x0c, 0xc0, 0xec, 0x72, 0x97, 0xa5, 0x94, 0xa7, 0x5a, 0x08, 0x79, 0x0d, 0x8d, 0x73, 0xda, 0xf3, 0x5c, 0xca, 0xbd, 0x30, 0xb0, 0x4f, 0xa9, 0xc3, 0xed, 0x18, 0x9d, 0x30, 0x76, 0xd1, 0x95, - 0x21, 0x5c, 0xda, 0xfa, 0xf1, 0x14, 0x0a, 0x7e, 0x99, 0x89, 0x78, 0x4a, 0x1d, 0xae, 0x35, 0xd5, - 0xcf, 0x07, 0xa0, 0x96, 0x16, 0x4b, 0x5e, 0x41, 0x3d, 0x8a, 0xc3, 0x2f, 0xd0, 0xe1, 0x52, 0x1f, - 0xeb, 0x2b, 0x5c, 0x98, 0x3a, 0x0e, 0x47, 0x4a, 0x80, 0x90, 0xcb, 0xb4, 0xba, 0xb5, 0x28, 0x07, - 0xcb, 0x94, 0x9d, 0x41, 0x43, 0xb9, 0x8d, 0x06, 0xb4, 0x77, 0xc1, 0x3c, 0x66, 0xd3, 0x0e, 0x0d, - 0xdc, 0x30, 0x40, 0x77, 0x86, 0x24, 0xcb, 0x3b, 0xb0, 0x2e, 0xe5, 0x6d, 0x6b, 0x71, 0xdb, 0xa9, - 0xb4, 0x56, 0x05, 0x16, 0x23, 0x7a, 0xd1, 0x0b, 0xa9, 0x6b, 0xfe, 0x08, 0x6e, 0x5d, 0x92, 0x2f, - 0xa2, 0x08, 0x95, 0x3d, 0xfd, 0x22, 0x94, 0xe7, 0xb6, 0x6b, 0x3e, 0xc8, 0x52, 0x35, 0x53, 0x37, - 0x89, 0xfc, 0xe7, 0x70, 0xfb, 0x52, 0xef, 0x93, 0xef, 0xc0, 0x92, 0x8c, 0xa5, 0x8b, 0xdd, 0x18, - 0x55, 0x82, 0x1b, 0x16, 0x08, 0xd0, 0x8e, 0x84, 0x98, 0x7b, 0x99, 0x89, 0xc3, 0xae, 0x24, 0x1f, - 0x02, 0xc9, 0xc7, 0xc7, 0x76, 0xc2, 0x24, 0xe0, 0x52, 0xc4, 0xbc, 0xb5, 0x9a, 0x73, 0xf2, 0x13, - 0x01, 0x37, 0xff, 0x6a, 0x40, 0x6d, 0xb0, 0x5b, 0x0d, 0x75, 0x38, 0x63, 0xa8, 0xc3, 0x91, 0x8f, - 0xa1, 0x24, 0x7b, 0x82, 0x68, 0x33, 0xc5, 0xf5, 0xa5, 0xad, 0xf5, 0x69, 0xab, 0xdc, 0xd2, 0x7c, - 0xe4, 0x18, 0x56, 0xb2, 0x02, 0x4e, 0x7c, 0x9f, 0xc6, 0x17, 0xba, 0x18, 0xee, 0x4f, 0x12, 0x95, - 0xc6, 0x42, 0x71, 0x58, 0xb5, 0x78, 0xe0, 0x6c, 0xfe, 0xc5, 0x18, 0xdb, 0x79, 0x45, 0x3b, 0xd7, - 0x23, 0xa5, 0x6f, 0xb8, 0xf1, 0xdf, 0x33, 0xbc, 0x70, 0x6d, 0xc3, 0xbf, 0x80, 0x77, 0x2e, 0x9b, - 0x18, 0xda, 0xf0, 0x7d, 0xa8, 0x9c, 0x49, 0xb8, 0x87, 0xa9, 0xed, 0xd3, 0x28, 0x4c, 0x73, 0xb8, - 0xcf, 0x6c, 0xfe, 0xc9, 0x80, 0xb7, 0xf7, 0x90, 0xeb, 0x9c, 0x51, 0x33, 0xf1, 0xba, 0x23, 0x4f, - 0x3c, 0x26, 0x74, 0x5a, 0xb1, 0x46, 0xf1, 0x5e, 0x51, 0x3c, 0x26, 0xd2, 0x33, 0x79, 0x1b, 0x2a, - 0x11, 0xed, 0xa2, 0xcd, 0xbc, 0x2f, 0xd5, 0x4b, 0x63, 0xc1, 0x2a, 0x0b, 0xc0, 0xb1, 0xf7, 0xa5, - 0x94, 0x2b, 0x91, 0x3c, 0x7c, 0x85, 0xe9, 0x40, 0x91, 0xe4, 0x2f, 0x05, 0xc0, 0xfc, 0xb3, 0x21, - 0x07, 0xf4, 0x90, 0xb1, 0x57, 0x8e, 0xd2, 0xbb, 0x00, 0xce, 0x19, 0x0d, 0xba, 0x68, 0xf7, 0x27, - 0x6a, 0x45, 0x41, 0x4e, 0x62, 0xef, 0x1b, 0xb3, 0xb6, 0x03, 0x6f, 0x69, 0x4b, 0xfb, 0xf5, 0x2c, - 0x9e, 0x70, 0x5a, 0x78, 0x5a, 0xfe, 0xfa, 0x48, 0x3e, 0x80, 0x95, 0x4e, 0x8c, 0xf4, 0x95, 0x90, - 0xa8, 0xab, 0x5c, 0x98, 0x6a, 0xc8, 0xd1, 0xa1, 0x11, 0xaa, 0xd6, 0x5b, 0x65, 0x28, 0xc5, 0xc8, - 0x92, 0x1e, 0x37, 0xff, 0x5e, 0x1c, 0xe7, 0x91, 0x5c, 0x8a, 0x5f, 0x51, 0xba, 0x99, 0xc3, 0x0a, - 0x97, 0x3b, 0xac, 0x38, 0xec, 0x30, 0xd1, 0xc3, 0x28, 0x53, 0xc8, 0x79, 0xdd, 0xc3, 0x28, 0x93, - 0xa8, 0x77, 0x87, 0x27, 0xb1, 0xf2, 0xca, 0xe0, 0x48, 0x35, 0x61, 0x39, 0x89, 0x5c, 0xca, 0xd1, - 0xb5, 0x29, 0x17, 0xaf, 0x13, 0x50, 0xaf, 0x13, 0x0d, 0xdc, 0xe6, 0x07, 0x8c, 0xfc, 0x02, 0x9a, - 0x31, 0x46, 0x21, 0xf3, 0x44, 0xc2, 0xda, 0xc3, 0x8e, 0x29, 0x69, 0xc7, 0x34, 0xfa, 0x34, 0xad, - 0x01, 0x17, 0x91, 0x47, 0xd0, 0x48, 0x7b, 0x9e, 0x72, 0x15, 0xb3, 0x9d, 0xd0, 0x8f, 0x7a, 0xc8, - 0xb1, 0xb1, 0x78, 0xcf, 0x58, 0x2f, 0x5b, 0xe9, 0xcc, 0xb2, 0x14, 0xfa, 0x89, 0xc6, 0x92, 0x76, - 0x2e, 0x1d, 0xca, 0xb2, 0xb4, 0x1e, 0x4c, 0x2a, 0xad, 0x91, 0x10, 0xe7, 0xb2, 0xe7, 0xfb, 0xb0, - 0x12, 0xe0, 0xaf, 0xb9, 0x9d, 0xcb, 0x92, 0x8a, 0x7a, 0x24, 0x09, 0xf0, 0x51, 0x9a, 0x29, 0xad, - 0x1b, 0xf0, 0x56, 0xee, 0xb2, 0x3a, 0xb4, 0xff, 0x28, 0xca, 0xf6, 0x35, 0x2e, 0xd9, 0xbf, 0x8d, - 0xed, 0xff, 0x79, 0x6c, 0xbf, 0x2e, 0x64, 0x33, 0x56, 0x37, 0xfd, 0xff, 0xc9, 0x60, 0x3e, 0x80, - 0x1b, 0x12, 0x69, 0x0f, 0x86, 0xb4, 0x24, 0x43, 0xba, 0x2a, 0x51, 0x27, 0xb9, 0xb8, 0x7e, 0x0f, - 0x6a, 0xd4, 0x71, 0x30, 0xea, 0x53, 0x2e, 0x4a, 0xca, 0x6a, 0x0a, 0x95, 0x54, 0xa3, 0x8b, 0x43, - 0x79, 0xdc, 0xe2, 0xf0, 0x2e, 0x2c, 0xa7, 0x41, 0x56, 0xef, 0x95, 0x8a, 0xec, 0xbe, 0x55, 0x0d, - 0x94, 0x6f, 0x15, 0xf2, 0x43, 0x58, 0xeb, 0xc4, 0xc2, 0xcd, 0xf6, 0x20, 0x2d, 0x48, 0x5a, 0xa2, - 0x70, 0x47, 0x39, 0x0e, 0xf3, 0x6b, 0x03, 0x96, 0x9e, 0x79, 0x57, 0xad, 0xb2, 0x3f, 0x81, 0x5b, - 0xf9, 0x9b, 0x84, 0xb1, 0x4d, 0x4f, 0x39, 0xc6, 0xd9, 0x26, 0xb5, 0x3f, 0x67, 0xdd, 0xe8, 0x5f, - 0xea, 0x79, 0xbc, 0x2d, 0xb0, 0x07, 0x8c, 0x6c, 0x00, 0xc9, 0xf8, 0x3a, 0x78, 0x1a, 0xc6, 0x28, - 0x58, 0xe4, 0xb2, 0xbd, 0x6f, 0x58, 0xab, 0x29, 0xae, 0x25, 0x51, 0x07, 0xd7, 0x1a, 0x2f, 0xad, - 0x3a, 0xac, 0x65, 0xba, 0x7a, 0xe1, 0xaf, 0x30, 0xb6, 0x3b, 0x61, 0x12, 0xb8, 0x03, 0xf0, 0x24, - 0x8a, 0x52, 0xb8, 0xf9, 0x1b, 0xa8, 0xaa, 0x8b, 0xeb, 0xee, 0xf1, 0x14, 0xca, 0x3a, 0xd8, 0xb3, - 0x3c, 0x21, 0xd2, 0x37, 0x4b, 0xc6, 0x3b, 0xae, 0x10, 0x0a, 0x63, 0x0a, 0xc1, 0x7c, 0x06, 0x37, - 0xf7, 0x90, 0xcb, 0x25, 0x5c, 0xaf, 0xde, 0xd7, 0x18, 0xda, 0xe6, 0xdf, 0x0c, 0xb8, 0x39, 0x76, - 0xa1, 0x17, 0x13, 0xf6, 0x1c, 0x63, 0xe6, 0x85, 0x81, 0x14, 0x58, 0xb4, 0xd2, 0x23, 0xd9, 0x86, - 0x05, 0x95, 0xeb, 0x6f, 0xf0, 0x6d, 0x42, 0x71, 0x92, 0xc7, 0x70, 0x1b, 0x4f, 0x4f, 0xd1, 0xe1, - 0xde, 0x39, 0xda, 0xa7, 0x71, 0xe8, 0x8b, 0xce, 0xe3, 0x7b, 0x3c, 0x57, 0x7f, 0xf5, 0x8c, 0xe0, - 0x69, 0x1c, 0xfa, 0x4f, 0x24, 0x5a, 0x54, 0x9c, 0x09, 0xcb, 0xca, 0xfc, 0xb4, 0x38, 0xe6, 0x55, - 0x67, 0xd4, 0x40, 0x51, 0x1b, 0xe6, 0x6f, 0x0d, 0xa8, 0x0f, 0x3b, 0x49, 0x87, 0x6b, 0xbc, 0x97, - 0xbe, 0x81, 0x8f, 0x20, 0xff, 0x32, 0xa0, 0xda, 0x66, 0x2c, 0xc1, 0xb4, 0x33, 0xdd, 0x82, 0x45, - 0x4e, 0xd9, 0xab, 0x7e, 0x5b, 0x2a, 0x89, 0x63, 0xdb, 0x15, 0x88, 0x24, 0xf0, 0x72, 0x4f, 0xc0, + 0x21, 0xac, 0x6e, 0xfd, 0x78, 0x0a, 0x05, 0xbf, 0xcc, 0x44, 0x3c, 0xa5, 0x0e, 0xd7, 0x9a, 0xea, + 0xe7, 0x03, 0x50, 0x4b, 0x8b, 0x25, 0xaf, 0xa0, 0x1e, 0xc5, 0xe1, 0x17, 0xe8, 0x70, 0xa9, 0x8f, + 0xf5, 0x15, 0x2e, 0x4c, 0x1d, 0x87, 0x23, 0x25, 0x40, 0xc8, 0x65, 0x5a, 0xdd, 0x5a, 0x94, 0x83, + 0x65, 0xca, 0xce, 0xa0, 0xa1, 0xdc, 0x46, 0x03, 0xda, 0xbb, 0x60, 0x1e, 0xb3, 0x69, 0x87, 0x06, + 0x6e, 0x18, 0xa0, 0x3b, 0x43, 0x92, 0xe5, 0x1d, 0x58, 0x97, 0xf2, 0xb6, 0xb5, 0xb8, 0xed, 0x54, + 0x5a, 0xab, 0x02, 0x8b, 0x11, 0xbd, 0xe8, 0x85, 0xd4, 0x35, 0x7f, 0x04, 0xb7, 0x2e, 0xc9, 0x17, + 0x51, 0x84, 0xca, 0x9e, 0x7e, 0x11, 0xca, 0x73, 0xdb, 0x35, 0x1f, 0x64, 0xa9, 0x9a, 0xa9, 0x9b, + 0x44, 0xfe, 0x73, 0xb8, 0x7d, 0xa9, 0xf7, 0xc9, 0x77, 0xa0, 0x2a, 0x63, 0xe9, 0x62, 0x37, 0x46, + 0x95, 0xe0, 0x86, 0x05, 0x02, 0xb4, 0x23, 0x21, 0xe6, 0x5e, 0x66, 0xe2, 0xb0, 0x2b, 0xc9, 0x87, + 0x40, 0xf2, 0xf1, 0xb1, 0x9d, 0x30, 0x09, 0xb8, 0x14, 0x31, 0x6f, 0xad, 0xe6, 0x9c, 0xfc, 0x44, + 0xc0, 0xcd, 0xbf, 0x18, 0x50, 0x1b, 0xec, 0x56, 0x43, 0x1d, 0xce, 0x18, 0xea, 0x70, 0xe4, 0x63, + 0x28, 0xc9, 0x9e, 0x20, 0xda, 0x4c, 0x71, 0xbd, 0xba, 0xb5, 0x3e, 0x6d, 0x95, 0x5b, 0x9a, 0x8f, + 0x1c, 0xc3, 0x4a, 0x56, 0xc0, 0x89, 0xef, 0xd3, 0xf8, 0x42, 0x17, 0xc3, 0xfd, 0x49, 0xa2, 0xd2, + 0x58, 0x28, 0x0e, 0xab, 0x16, 0x0f, 0x9c, 0xcd, 0x3f, 0x1b, 0x63, 0x3b, 0xaf, 0x68, 0xe7, 0x7a, + 0xa4, 0xf4, 0x0d, 0x37, 0xfe, 0x73, 0x86, 0x17, 0xae, 0x6d, 0xf8, 0x17, 0xf0, 0xce, 0x65, 0x13, + 0x43, 0x1b, 0xbe, 0x0f, 0x95, 0x33, 0x09, 0xf7, 0x30, 0xb5, 0x7d, 0x1a, 0x85, 0x69, 0x0e, 0xf7, + 0x99, 0xcd, 0x3f, 0x1a, 0xf0, 0xf6, 0x1e, 0x72, 0x9d, 0x33, 0x6a, 0x26, 0x5e, 0x77, 0xe4, 0x89, + 0xc7, 0x84, 0x4e, 0x2b, 0xd6, 0x28, 0xde, 0x2b, 0x8a, 0xc7, 0x44, 0x7a, 0x26, 0x6f, 0x43, 0x25, + 0xa2, 0x5d, 0xb4, 0x99, 0xf7, 0xa5, 0x7a, 0x69, 0x2c, 0x58, 0x65, 0x01, 0x38, 0xf6, 0xbe, 0x94, + 0x72, 0x25, 0x92, 0x87, 0xaf, 0x30, 0x1d, 0x28, 0x92, 0xfc, 0xa5, 0x00, 0x98, 0x7f, 0x32, 0xe4, + 0x80, 0x1e, 0x32, 0xf6, 0xca, 0x51, 0x7a, 0x17, 0xc0, 0x39, 0xa3, 0x41, 0x17, 0xed, 0xfe, 0x44, + 0xad, 0x28, 0xc8, 0x49, 0xec, 0x7d, 0x6b, 0xd6, 0x7e, 0x6d, 0xc0, 0x5b, 0xda, 0xd4, 0x7e, 0x41, + 0x8b, 0x37, 0x9c, 0x96, 0x9e, 0xd6, 0xbf, 0x3e, 0x92, 0x0f, 0x60, 0xa5, 0x13, 0x23, 0x7d, 0x25, + 0x44, 0xea, 0x32, 0x17, 0xb6, 0x1a, 0x72, 0x76, 0x68, 0x84, 0x2a, 0x76, 0x31, 0xde, 0xd3, 0x8a, + 0xe6, 0x17, 0x91, 0x1e, 0xdd, 0x56, 0x55, 0xc3, 0x5e, 0x5e, 0x44, 0xd8, 0x2a, 0x43, 0x29, 0x46, + 0x96, 0xf4, 0xb8, 0xf9, 0xb7, 0xe2, 0x38, 0xaf, 0xe5, 0xca, 0xe0, 0x8a, 0xf2, 0xce, 0x9c, 0x5a, + 0xb8, 0xdc, 0xa9, 0xc5, 0x61, 0xa7, 0x8a, 0x3e, 0x47, 0x99, 0x42, 0xce, 0xeb, 0x3e, 0x47, 0x99, + 0x44, 0xbd, 0x3b, 0x3c, 0xad, 0x95, 0xe7, 0x06, 0xc7, 0xae, 0x09, 0xcb, 0x49, 0xe4, 0x52, 0x8e, + 0xae, 0x4d, 0xb9, 0x78, 0xc1, 0x80, 0x7a, 0xc1, 0x68, 0xe0, 0x36, 0x3f, 0x60, 0xe4, 0x17, 0xd0, + 0x8c, 0x31, 0x0a, 0x99, 0x27, 0x92, 0xda, 0x1e, 0xf6, 0x5d, 0x49, 0xfb, 0xae, 0xd1, 0xa7, 0x69, + 0x0d, 0x7a, 0xf1, 0x11, 0x34, 0x52, 0x2f, 0x2a, 0x57, 0x31, 0xdb, 0x09, 0xfd, 0xa8, 0x87, 0x1c, + 0x1b, 0x8b, 0xf7, 0x8c, 0xf5, 0xb2, 0x95, 0xce, 0x35, 0x4b, 0xa1, 0x9f, 0x68, 0x2c, 0x69, 0xe7, + 0x52, 0xa6, 0x2c, 0xcb, 0xef, 0xc1, 0xa4, 0xf2, 0x1b, 0xc9, 0x82, 0x5c, 0x86, 0x7d, 0x1f, 0x56, + 0x02, 0xfc, 0x35, 0xb7, 0x73, 0x99, 0x54, 0x51, 0x0f, 0x29, 0x01, 0x3e, 0x4a, 0xb3, 0xa9, 0x75, + 0x03, 0xde, 0xca, 0x5d, 0x56, 0x87, 0xf6, 0xef, 0x45, 0xd9, 0xe2, 0xc6, 0x15, 0xc4, 0xff, 0x63, + 0xfb, 0x3f, 0x1e, 0xdb, 0x6f, 0x0a, 0xd9, 0x1c, 0xd6, 0x83, 0xe1, 0xbf, 0x32, 0x98, 0x0f, 0xe0, + 0x86, 0x44, 0xda, 0x83, 0x21, 0x2d, 0xc9, 0x90, 0xae, 0x4a, 0xd4, 0x49, 0x2e, 0xae, 0xdf, 0x83, + 0x1a, 0x75, 0x1c, 0x8c, 0xfa, 0x94, 0x8b, 0x92, 0x72, 0x29, 0x85, 0x4a, 0xaa, 0xd1, 0xe5, 0xa2, + 0x3c, 0x6e, 0xb9, 0x78, 0x17, 0x96, 0xd3, 0x20, 0xab, 0x37, 0x4d, 0x45, 0x76, 0xe8, 0xb4, 0x37, + 0xca, 0xf7, 0x0c, 0xf9, 0x21, 0xac, 0x75, 0x62, 0xe1, 0x66, 0x7b, 0x90, 0x16, 0x24, 0x2d, 0x51, + 0xb8, 0xa3, 0x1c, 0x87, 0xf9, 0x8d, 0x01, 0xd5, 0x67, 0xde, 0x55, 0xeb, 0xee, 0x4f, 0xe0, 0x56, + 0xfe, 0x26, 0x61, 0x6c, 0xd3, 0x53, 0x8e, 0x71, 0xb6, 0x6d, 0xed, 0xcf, 0x59, 0x37, 0xfa, 0x97, + 0x7a, 0x1e, 0x6f, 0x0b, 0xec, 0x01, 0x23, 0x1b, 0x40, 0x32, 0xbe, 0x0e, 0x9e, 0x86, 0x31, 0x0a, + 0x16, 0xb9, 0x90, 0xef, 0x1b, 0xd6, 0x6a, 0x8a, 0x6b, 0x49, 0xd4, 0xc1, 0xb5, 0x46, 0x50, 0xab, + 0x0e, 0x6b, 0x99, 0xae, 0x5e, 0xf8, 0x2b, 0x8c, 0xed, 0x4e, 0x98, 0x04, 0xee, 0x00, 0x3c, 0x89, + 0xa2, 0x14, 0x6e, 0xfe, 0x06, 0x96, 0xd4, 0xc5, 0x75, 0xf7, 0x78, 0x0a, 0x65, 0x1d, 0xec, 0x59, + 0x9e, 0x19, 0xe9, 0xbb, 0x26, 0xe3, 0x1d, 0x57, 0x08, 0x85, 0x31, 0x85, 0x60, 0x3e, 0x83, 0x9b, + 0x7b, 0xc8, 0xe5, 0xa2, 0xae, 0xd7, 0xf3, 0x6b, 0x0c, 0x76, 0xf3, 0xaf, 0x06, 0xdc, 0x1c, 0xbb, + 0xf4, 0x8b, 0x21, 0x7c, 0x8e, 0x31, 0xf3, 0xc2, 0x40, 0x0a, 0x2c, 0x5a, 0xe9, 0x91, 0x6c, 0xc3, + 0x82, 0xca, 0xf5, 0x37, 0xf8, 0x7e, 0xa1, 0x38, 0xc9, 0x63, 0xb8, 0x8d, 0xa7, 0xa7, 0xe8, 0x70, + 0xef, 0x1c, 0xed, 0xd3, 0x38, 0xf4, 0x45, 0xe7, 0xf1, 0x3d, 0x9e, 0xab, 0xbf, 0x7a, 0x46, 0xf0, + 0x34, 0x0e, 0xfd, 0x27, 0x12, 0x2d, 0x2a, 0xce, 0x84, 0x65, 0x65, 0x7e, 0x5a, 0x1c, 0xf3, 0xaa, + 0x33, 0x6a, 0xa0, 0xa8, 0x0d, 0xf3, 0xb7, 0x06, 0xd4, 0x87, 0x9d, 0xa4, 0xc3, 0x35, 0xde, 0x4b, + 0xdf, 0xc2, 0x87, 0x92, 0x7f, 0x1a, 0xb0, 0xd4, 0x66, 0x2c, 0xc1, 0xb4, 0x33, 0xdd, 0x82, 0x45, + 0x4e, 0xd9, 0xab, 0x7e, 0x5b, 0x2a, 0x89, 0x63, 0xdb, 0x15, 0x88, 0x24, 0xf0, 0x72, 0xcf, 0xc4, 0x92, 0x38, 0xb6, 0x5d, 0x52, 0x87, 0x12, 0x93, 0x82, 0xb5, 0x4b, 0xf4, 0x49, 0xf4, 0x93, 0x53, - 0x2f, 0x16, 0x2d, 0x07, 0x31, 0xb0, 0xb3, 0x3d, 0x48, 0xb5, 0xa6, 0x55, 0x89, 0x3a, 0x46, 0x0c, - 0x5a, 0x6a, 0x21, 0x1a, 0x6a, 0x89, 0x0b, 0xc3, 0x2d, 0x31, 0xbf, 0x4a, 0x95, 0x06, 0x56, 0xa9, + 0x2f, 0x16, 0x2d, 0x07, 0x31, 0xb0, 0xb3, 0x5d, 0x49, 0xb5, 0xa6, 0x55, 0x89, 0x3a, 0x46, 0x0c, + 0x5a, 0x6a, 0x69, 0x1a, 0x6a, 0x89, 0x0b, 0xc3, 0x2d, 0x31, 0xbf, 0x6e, 0x95, 0x06, 0xd6, 0xad, 0x51, 0x5f, 0x2f, 0x8e, 0xfa, 0xda, 0x83, 0x9b, 0xa2, 0x1e, 0x9e, 0x47, 0x18, 0xc8, 0xeb, 0x5e, - 0x91, 0x8f, 0x03, 0xa5, 0x5a, 0x98, 0x58, 0xaa, 0xc5, 0xe1, 0x97, 0xe0, 0x57, 0x06, 0xd4, 0x87, - 0x75, 0xf5, 0x57, 0x10, 0x4f, 0x42, 0xa6, 0x59, 0x41, 0xf2, 0x61, 0xb1, 0x34, 0xdf, 0xb4, 0xf5, + 0x91, 0x8f, 0x03, 0xa5, 0x5a, 0x98, 0x58, 0xaa, 0xc5, 0xe1, 0xd7, 0xe2, 0x57, 0x06, 0xd4, 0x87, + 0x75, 0xf5, 0xd7, 0x14, 0x4f, 0x42, 0xa6, 0x59, 0x53, 0xf2, 0x61, 0xb1, 0x34, 0xdf, 0xb4, 0xf5, 0x77, 0xbf, 0x07, 0x2b, 0x43, 0xc1, 0x27, 0x77, 0xa0, 0xf1, 0xe2, 0x64, 0xf7, 0x64, 0xd7, 0xde, 0x3d, 0xdc, 0x6e, 0x3d, 0xdb, 0x3d, 0xd8, 0x3d, 0x7c, 0x69, 0x9f, 0x1c, 0x7e, 0x72, 0xf8, 0xfc, 0xd3, 0xc3, 0xd5, 0xb9, 0xb1, 0x58, 0xf5, 0x73, 0x67, 0xd5, 0x20, 0x77, 0xe1, 0xf6, 0x08, 0x76, - 0xa7, 0x7d, 0xac, 0xd0, 0x85, 0xad, 0x7f, 0x96, 0xa1, 0x72, 0x9c, 0x1a, 0x4e, 0x3e, 0x87, 0xf9, - 0x23, 0x2f, 0xe8, 0x92, 0xf7, 0x27, 0x4e, 0xdb, 0xfe, 0x47, 0xd1, 0xe6, 0xfa, 0xd5, 0x84, 0xca, - 0x81, 0xe6, 0x1c, 0xa1, 0x50, 0x52, 0x9f, 0x0a, 0xc9, 0x07, 0x13, 0x9d, 0x97, 0xff, 0x84, 0xd9, - 0xbc, 0x3f, 0x0d, 0x69, 0xa6, 0xe2, 0x0f, 0x86, 0x6c, 0x5e, 0xa3, 0xab, 0x24, 0x79, 0x34, 0x49, - 0xce, 0xa4, 0x8f, 0x89, 0xcd, 0xc7, 0x6f, 0xc0, 0x99, 0x19, 0xf4, 0x47, 0xd5, 0x28, 0xc6, 0xec, - 0x88, 0x64, 0x56, 0xb9, 0xfd, 0xf5, 0xa9, 0xf9, 0xb3, 0x37, 0x61, 0xcd, 0x6c, 0xfa, 0xbd, 0x01, - 0x6b, 0xe3, 0x76, 0x11, 0xf2, 0xd3, 0x2b, 0xc4, 0x5e, 0xb6, 0x7c, 0x36, 0x1f, 0xcd, 0xce, 0x38, + 0xa7, 0x7d, 0xac, 0xd0, 0x85, 0xad, 0x7f, 0x94, 0xa1, 0x72, 0x9c, 0x1a, 0x4e, 0x3e, 0x87, 0xf9, + 0x23, 0x2f, 0xe8, 0x92, 0xf7, 0x27, 0x4e, 0xdb, 0xfe, 0x87, 0xd3, 0xe6, 0xfa, 0xd5, 0x84, 0xca, + 0x81, 0xe6, 0x1c, 0xa1, 0x50, 0x52, 0x9f, 0x13, 0xc9, 0x07, 0x13, 0x9d, 0x97, 0xff, 0xcc, 0xd9, + 0xbc, 0x3f, 0x0d, 0x69, 0xa6, 0xe2, 0xf7, 0x86, 0x6c, 0x5e, 0xa3, 0xeb, 0x26, 0x79, 0x34, 0x49, + 0xce, 0xa4, 0x0f, 0x8e, 0xcd, 0xc7, 0x6f, 0xc0, 0x99, 0x19, 0xf4, 0x07, 0xd5, 0x28, 0xc6, 0xec, + 0x91, 0x64, 0x56, 0xb9, 0xfd, 0x15, 0xab, 0xf9, 0xb3, 0x37, 0x61, 0xcd, 0x6c, 0xfa, 0x9d, 0x01, + 0x6b, 0xe3, 0x76, 0x11, 0xf2, 0xd3, 0x2b, 0xc4, 0x5e, 0xb6, 0xa0, 0x36, 0x1f, 0xcd, 0xce, 0x38, 0x1c, 0xb2, 0xd1, 0xe7, 0x33, 0x99, 0x51, 0x6a, 0xce, 0x3f, 0x8f, 0xdf, 0x80, 0x33, 0x33, 0xe8, 0x73, 0x98, 0x17, 0x3d, 0x60, 0x72, 0x0d, 0xe4, 0x9e, 0x26, 0x93, 0x6b, 0x20, 0x3f, 0xca, 0xcd, 0x39, 0x72, 0x01, 0xb5, 0xc1, 0xb9, 0x41, 0x1e, 0x5e, 0x61, 0xeb, 0xe8, 0x20, 0x6e, 0x6e, 0xcd, 0xc2, 0x92, 0x57, 0x3d, 0xd8, 0xdb, 0x26, 0xab, 0x1e, 0xdb, 0x73, 0x27, 0xab, 0x1e, 0xdf, 0x3a, 0xcd, 0xb9, 0x16, 0xc2, 0x3b, 0x4e, 0xe8, 0x4f, 0x60, 0x6d, 0xd5, 0xb2, 0x1e, 0x74, 0x14, 0x87, 0x3c, 0x3c, 0x32, 0x3e, 0x7b, 0xd8, 0xf5, 0xf8, 0x59, 0xd2, 0xd9, 0x70, 0x42, 0x7f, 0x53, 0x30, - 0x6e, 0xe6, 0x18, 0x37, 0x69, 0xe4, 0x6d, 0x66, 0xcc, 0x9b, 0xf2, 0xaf, 0x9c, 0xa8, 0xd3, 0x29, - 0xc9, 0x1f, 0x1f, 0xfd, 0x27, 0x00, 0x00, 0xff, 0xff, 0x9b, 0x2a, 0xb4, 0x32, 0xe6, 0x19, 0x00, + 0x6e, 0xe6, 0x18, 0x37, 0x69, 0xe4, 0x6d, 0x66, 0xcc, 0x9b, 0xf2, 0xef, 0x9e, 0xa8, 0xd3, 0x29, + 0xc9, 0x1f, 0x1f, 0xfd, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x1b, 0xa5, 0xdd, 0x0a, 0x1a, 0x00, 0x00, }, } diff --git a/service/stovepipe/server/mapper/project_status.go b/service/stovepipe/server/mapper/project_status.go index dbdbbc556..a3e3d8962 100644 --- a/service/stovepipe/server/mapper/project_status.go +++ b/service/stovepipe/server/mapper/project_status.go @@ -90,7 +90,8 @@ func validationFactsToProto(facts []entity.ValidationFact) []*pb.ProjectValidati for _, fact := range facts { degree := fact.Degree projects = append(projects, &pb.ProjectValidation{ - Project: fact.Project, + Project: fact.Project, + ProjectType: fact.ProjectType, Result: &pb.ProjectValidation_BreakageDegree{ BreakageDegree: degree, }, diff --git a/service/stovepipe/server/mapper/project_status_test.go b/service/stovepipe/server/mapper/project_status_test.go index 701cf815e..d136f35a5 100644 --- a/service/stovepipe/server/mapper/project_status_test.go +++ b/service/stovepipe/server/mapper/project_status_test.go @@ -35,7 +35,7 @@ func TestProjectStatusRequestAndResponseMapping(t *testing.T) { RequestSummary: entity.RequestSummary{RequestID: "1", Queue: "queue", URI: "uri", BaseURI: "base", State: entity.RequestStateSucceeded}, RepositoryValidationFact: entity.ValidationFact{Degree: degree}, HasRepositoryValidationFact: true, - ProjectValidationFacts: []entity.ValidationFact{{Project: "project-a", Degree: entity.DegreeBroken}}, + ProjectValidationFacts: []entity.ValidationFact{{Project: "project-a", ProjectType: "service", Degree: entity.DegreeBroken}}, NextPageToken: "next-token", }) assert.Equal(t, "1", response.GetRequestId()) @@ -47,6 +47,7 @@ func TestProjectStatusRequestAndResponseMapping(t *testing.T) { return } assert.Equal(t, "project-a", response.GetProjects()[0].GetProject()) + assert.Equal(t, "service", response.GetProjects()[0].GetProjectType()) assert.Equal(t, entity.DegreeBroken, response.GetProjects()[0].GetBreakageDegree()) assert.IsType(t, &pb.ProjectValidation_BreakageDegree{}, response.GetProjects()[0].GetResult()) assert.Equal(t, "next-token", response.GetNextPageToken()) @@ -59,8 +60,9 @@ func TestProjectStatusMappingPreservesExplicitGreenResults(t *testing.T) { Degree: entity.DegreeGreen, }, ProjectValidationFacts: []entity.ValidationFact{{ - Project: "project-a", - Degree: entity.DegreeGreen, + Project: "project-a", + ProjectType: "service", + Degree: entity.DegreeGreen, }}, }) @@ -83,7 +85,7 @@ func TestProjectStatusByIDRequestAndResponseMapping(t *testing.T) { HasRepositoryValidationFact: hasFact, RepositoryValidationFact: entity.ValidationFact{Degree: entity.DegreeGreen}, ProjectResultsComplete: true, - ProjectValidationFacts: []entity.ValidationFact{{Project: "project-a", Degree: entity.DegreeGreen}, {Project: "project-b", Degree: entity.DegreeBroken}}, + ProjectValidationFacts: []entity.ValidationFact{{Project: "project-a", ProjectType: "service", Degree: entity.DegreeGreen}, {Project: "project-b", Degree: entity.DegreeBroken}}, NextPageToken: "next-token", UpdatedAtMs: 123, } @@ -104,6 +106,8 @@ func TestProjectStatusByIDRequestAndResponseMapping(t *testing.T) { } if assert.Len(t, response.Projects, 2) { assert.Equal(t, "project-a", response.Projects[0].GetProject()) + assert.Equal(t, "service", response.Projects[0].GetProjectType()) + assert.Empty(t, response.Projects[1].GetProjectType()) assert.IsType(t, &pb.ProjectValidation_BreakageDegree{}, response.Projects[0].GetResult()) assert.Equal(t, entity.DegreeGreen, response.Projects[0].GetBreakageDegree()) assert.Equal(t, "project-b", response.Projects[1].GetProject()) diff --git a/stovepipe/controller/record/record.go b/stovepipe/controller/record/record.go index 7f80d7b39..55d31af43 100644 --- a/stovepipe/controller/record/record.go +++ b/stovepipe/controller/record/record.go @@ -212,11 +212,12 @@ func (c *Controller) persistProjectFacts(ctx context.Context, store storage.Stor brokenCount := 0 for _, result := range results { fact, _, err := recordValidationFact(ctx, c.logger, c.metricsScope, store.GetValidationFactStore(), entity.ValidationFact{ - URI: request.URI, - Project: result.Project, - Degree: result.Degree, - RequestID: request.ID, - CreatedAt: time.Now().UnixMilli(), + URI: request.URI, + Project: result.ProjectInfo.Name, + ProjectType: result.ProjectInfo.ProjectType, + Degree: result.Degree, + RequestID: request.ID, + CreatedAt: time.Now().UnixMilli(), }) if err != nil { return err @@ -240,15 +241,15 @@ func (c *Controller) persistProjectFacts(ctx context.Context, store storage.Stor func validateProjectStatusResults(request entity.Request, results []projectstatus.Result) error { seen := make(map[string]struct{}, len(results)) for _, result := range results { - if result.Project == "" { + if result.ProjectInfo.Name == "" { return fmt.Errorf("project status result for request %q has an empty project", request.ID) } - if _, ok := seen[result.Project]; ok { - return fmt.Errorf("project status result for request %q contains duplicate project %q", request.ID, result.Project) + if _, ok := seen[result.ProjectInfo.Name]; ok { + return fmt.Errorf("project status result for request %q contains duplicate project %q", request.ID, result.ProjectInfo.Name) } - seen[result.Project] = struct{}{} + seen[result.ProjectInfo.Name] = struct{}{} if math.IsNaN(result.Degree) || result.Degree < entity.DegreeGreen || result.Degree > entity.DegreeBroken { - return fmt.Errorf("project status result for request %q and project %q has invalid degree %v", request.ID, result.Project, result.Degree) + return fmt.Errorf("project status result for request %q and project %q has invalid degree %v", request.ID, result.ProjectInfo.Name, result.Degree) } } return nil diff --git a/stovepipe/controller/record/record_test.go b/stovepipe/controller/record/record_test.go index 57aca5d62..1e99f656b 100644 --- a/stovepipe/controller/record/record_test.go +++ b/stovepipe/controller/record/record_test.go @@ -337,8 +337,8 @@ func TestProcess_RecordsNamedProjectStatusResults(t *testing.T) { projectStatusResolver.EXPECT().Resolve( gomock.Any(), requestWithState(entity.RequestStateFailed), "bk-1", ).Return([]projectstatus.Result{ - {Project: "project-a", Degree: entity.DegreeBroken}, - {Project: "project-b", Degree: entity.DegreeGreen}, + {ProjectInfo: projectstatus.ProjectInfo{Name: "project-a"}, Degree: entity.DegreeBroken}, + {ProjectInfo: projectstatus.ProjectInfo{Name: "project-b"}, Degree: entity.DegreeGreen}, }, nil) m.reqStore.EXPECT().Get(gomock.Any(), testID).Return(requestWithState(entity.RequestStateFailed), nil) @@ -414,8 +414,8 @@ func TestRecordProjectFacts_PassesEmptyTerminalBuild(t *testing.T) { func TestResolveProjectResultsMetrics(t *testing.T) { validResults := []projectstatus.Result{ - {Project: "project-a", Degree: entity.DegreeGreen}, - {Project: "project-b", Degree: entity.DegreeBroken}, + {ProjectInfo: projectstatus.ProjectInfo{Name: "project-a"}, Degree: entity.DegreeGreen}, + {ProjectInfo: projectstatus.ProjectInfo{Name: "project-b"}, Degree: entity.DegreeBroken}, } for _, tt := range []struct { name string @@ -434,7 +434,7 @@ func TestResolveProjectResultsMetrics(t *testing.T) { {name: "resolver cancellation", resolveErr: context.Canceled, wantResult: "cancel"}, {name: "resolver deadline", resolveErr: context.DeadlineExceeded, wantResult: "error"}, {name: "invalid project", results: []projectstatus.Result{{Degree: entity.DegreeGreen}}, invalid: true, wantResult: "error"}, - {name: "invalid degree", results: []projectstatus.Result{{Project: "project-a", Degree: math.NaN()}}, invalid: true, wantResult: "error"}, + {name: "invalid degree", results: []projectstatus.Result{{ProjectInfo: projectstatus.ProjectInfo{Name: "project-a"}, Degree: math.NaN()}}, invalid: true, wantResult: "error"}, {name: "duplicate projects", results: []projectstatus.Result{validResults[0], validResults[0]}, invalid: true, wantResult: "error"}, } { t.Run(tt.name, func(t *testing.T) { @@ -509,14 +509,14 @@ func TestPersistProjectFactsMetrics(t *testing.T) { var results []projectstatus.Result if !tt.empty { results = []projectstatus.Result{ - {Project: "project-a", Degree: entity.DegreeGreen}, - {Project: "project-b", Degree: entity.DegreeBroken}, + {ProjectInfo: projectstatus.ProjectInfo{Name: "project-a"}, Degree: entity.DegreeGreen}, + {ProjectInfo: projectstatus.ProjectInfo{Name: "project-b"}, Degree: entity.DegreeBroken}, } } materializer := requestlogmock.NewMockMaterializer(ctrl) c.materializer = materializer for _, result := range results { - fact := entity.ValidationFact{URI: request.URI, Project: result.Project, Degree: result.Degree, RequestID: request.ID, CreatedAt: testChangeTime.UnixMilli()} + fact := entity.ValidationFact{URI: request.URI, Project: result.ProjectInfo.Name, Degree: result.Degree, RequestID: request.ID, CreatedAt: testChangeTime.UnixMilli()} createErr := tt.factErr if tt.existing { createErr = storage.ErrAlreadyExists @@ -527,7 +527,7 @@ func TestPersistProjectFactsMetrics(t *testing.T) { } indexErr := tt.indexErr if tt.existing { - m.factStore.EXPECT().Get(gomock.Any(), request.URI, result.Project).Return(fact, nil) + m.factStore.EXPECT().Get(gomock.Any(), request.URI, result.ProjectInfo.Name).Return(fact, nil) indexErr = storage.ErrAlreadyExists } entry, err := entity.NewProjectFactByDegree(fact) @@ -641,8 +641,8 @@ func TestProcess_RejectsInvalidProjectStatusResultsBeforeWritingFacts(t *testing projectStatusResolverFactory.EXPECT().For(projectstatus.ResolverConfig{QueueName: testQueue}). Return(projectStatusResolver, nil) projectStatusResolver.EXPECT().Resolve(gomock.Any(), gomock.Any(), gomock.Any()).Return([]projectstatus.Result{ - {Project: "project-a", Degree: entity.DegreeBroken}, - {Project: "project-b", Degree: math.NaN()}, + {ProjectInfo: projectstatus.ProjectInfo{Name: "project-a"}, Degree: entity.DegreeBroken}, + {ProjectInfo: projectstatus.ProjectInfo{Name: "project-b"}, Degree: math.NaN()}, }, nil) m.reqStore.EXPECT().Get(gomock.Any(), testID).Return(requestWithState(entity.RequestStateFailed), nil) @@ -1388,8 +1388,8 @@ func TestPersistProjectFactsRepairsIndexOnRetry(t *testing.T) { return nil }), ) - require.ErrorIs(t, c.persistProjectFacts(queueContext(), m.store, request, []projectstatus.Result{{Project: "p", Degree: 0.25}}), writeErr) - require.NoError(t, c.persistProjectFacts(queueContext(), m.store, request, []projectstatus.Result{{Project: "p", Degree: 0.75}})) + require.ErrorIs(t, c.persistProjectFacts(queueContext(), m.store, request, []projectstatus.Result{{ProjectInfo: projectstatus.ProjectInfo{Name: "p"}, Degree: 0.25}}), writeErr) + require.NoError(t, c.persistProjectFacts(queueContext(), m.store, request, []projectstatus.Result{{ProjectInfo: projectstatus.ProjectInfo{Name: "p"}, Degree: 0.75}})) snapshot := m.metricsScope.Snapshot() assert.EqualValues(t, 2, snapshot.Counters()["record_controller.persist_project_facts.start+queue="+testQueue].Value()) for _, result := range []string{"success", "error"} { @@ -1411,9 +1411,9 @@ func TestProcessRecordsDegreeAfterResolvingProjects(t *testing.T) { {name: "successful repository", state: entity.RequestStateSucceeded, degree: entity.DegreeGreen}, {name: "failed repository without project results", state: entity.RequestStateFailed, degree: entity.DegreeBroken}, {name: "failed repository with project results", state: entity.RequestStateFailed, degree: entity.DegreePartial, - results: []projectstatus.Result{{Project: "a", Degree: entity.DegreeGreen}, {Project: "b", Degree: 0.25}}}, + results: []projectstatus.Result{{ProjectInfo: projectstatus.ProjectInfo{Name: "a", ProjectType: "service"}, Degree: entity.DegreeGreen}, {ProjectInfo: projectstatus.ProjectInfo{Name: "b", ProjectType: "batch"}, Degree: 0.25}}}, {name: "successful repository with project results", state: entity.RequestStateSucceeded, degree: entity.DegreeGreen, - results: []projectstatus.Result{{Project: "a", Degree: entity.DegreeGreen}}}, + results: []projectstatus.Result{{ProjectInfo: projectstatus.ProjectInfo{Name: "a", ProjectType: "service"}, Degree: entity.DegreeGreen}}}, } { t.Run(tt.name, func(t *testing.T) { ctrl := gomock.NewController(t) @@ -1442,7 +1442,7 @@ func TestProcessRecordsDegreeAfterResolvingProjects(t *testing.T) { }).Times(len(tt.results) + 1) for _, result := range tt.results { m.indexStore.EXPECT().Create(gomock.Any(), entity.ProjectFactByDegree{ - URI: testURI, Degree: result.Degree, Project: result.Project, + URI: testURI, Degree: result.Degree, Project: result.ProjectInfo.Name, }).Return(nil) } if tt.degree == entity.DegreeGreen { @@ -1457,7 +1457,8 @@ func TestProcessRecordsDegreeAfterResolvingProjects(t *testing.T) { require.NoError(t, c.Process(queueContext(), delivery(t, ctrl, recordPayload(t, testID)))) assert.Equal(t, tt.degree, facts[""].Degree) for _, result := range tt.results { - assert.Equal(t, result.Degree, facts[result.Project].Degree) + assert.Equal(t, result.Degree, facts[result.ProjectInfo.Name].Degree) + assert.Equal(t, result.ProjectInfo.ProjectType, facts[result.ProjectInfo.Name].ProjectType) } assert.Len(t, m.hooks.events, 1) }) @@ -1477,7 +1478,7 @@ func TestProcessRetriesProjectPersistenceWithImmutablePartialFact(t *testing.T) c.projectStatusResolverFactory = factory factory.EXPECT().For(projectstatus.ResolverConfig{QueueName: testQueue}).Return(resolver, nil).Times(2) resolver.EXPECT().Resolve(gomock.Any(), request, request.TerminalBuildID).Return( - []projectstatus.Result{{Project: "a", Degree: entity.DegreeBroken}}, nil).Times(2) + []projectstatus.Result{{ProjectInfo: projectstatus.ProjectInfo{Name: "a"}, Degree: entity.DegreeBroken}}, nil).Times(2) var stored entity.ValidationFact m.factStore.EXPECT().Create(gomock.Any(), gomock.Any()).DoAndReturn(func(_ context.Context, fact entity.ValidationFact) error { assert.Empty(t, fact.Project) @@ -1536,7 +1537,7 @@ func TestPersistProjectFactsCountsPersistedOutcomes(t *testing.T) { }, log.Metadata) return nil }) - require.NoError(t, c.persistProjectFacts(queueContext(), m.store, request, []projectstatus.Result{{Project: "p", Degree: tt.resolvedDegree}})) + require.NoError(t, c.persistProjectFacts(queueContext(), m.store, request, []projectstatus.Result{{ProjectInfo: projectstatus.ProjectInfo{Name: "p"}, Degree: tt.resolvedDegree}})) }) } } diff --git a/stovepipe/entity/validation_fact.go b/stovepipe/entity/validation_fact.go index 9fe9fbbfc..cadb895cd 100644 --- a/stovepipe/entity/validation_fact.go +++ b/stovepipe/entity/validation_fact.go @@ -42,6 +42,8 @@ type ValidationFact struct { // Project is the scope the degree applies to. Empty means the whole // repository; named projects narrow the fact to part of it. Project string `json:"project"` + // ProjectType is a provider-defined classification recorded with the fact; empty means unknown. + ProjectType string `json:"project_type"` // Degree is how broken the scope was, on the closed interval // [DegreeGreen, DegreeBroken]. A failed repository with project results has // DegreePartial; without project results it has DegreeBroken. diff --git a/stovepipe/extension/projectstatus/projectstatus.go b/stovepipe/extension/projectstatus/projectstatus.go index c3a86a749..e98e78684 100644 --- a/stovepipe/extension/projectstatus/projectstatus.go +++ b/stovepipe/extension/projectstatus/projectstatus.go @@ -24,11 +24,18 @@ import ( "github.com/uber/submitqueue/stovepipe/entity" ) -// Result is one project status result. The record stage supplies the request -// identity and recording timestamp when it persists this result. +// ProjectInfo identifies a project and its provider-defined metadata. +type ProjectInfo struct { + // Name identifies the project within its repository. + Name string + // ProjectType is a provider-defined classification; empty means unknown. + ProjectType string +} + +// Result is one project status result. type Result struct { - // Project identifies the project to which this result applies. - Project string + // ProjectInfo identifies and describes the project. + ProjectInfo ProjectInfo // Degree describes how broken the project is, on the closed interval [0, 1]. Degree float64 } diff --git a/stovepipe/extension/storage/mysql/README.md b/stovepipe/extension/storage/mysql/README.md new file mode 100644 index 000000000..0b1cf38cb --- /dev/null +++ b/stovepipe/extension/storage/mysql/README.md @@ -0,0 +1,6 @@ +# Schema upgrades + +Before deploying application code that reads or writes project types, existing databases +must add `project_type` as the last column in `validation_fact`, with type `VARCHAR(255)`, +`NOT NULL`, and an empty-string default. Existing facts retain an unknown project type; +their immutable recorded results are preserved. Fresh databases use `schema/`. diff --git a/stovepipe/extension/storage/mysql/schema/validation_fact.sql b/stovepipe/extension/storage/mysql/schema/validation_fact.sql index a1eea8b25..e8aedc75d 100644 --- a/stovepipe/extension/storage/mysql/schema/validation_fact.sql +++ b/stovepipe/extension/storage/mysql/schema/validation_fact.sql @@ -11,5 +11,6 @@ CREATE TABLE IF NOT EXISTS validation_fact ( degree DOUBLE NOT NULL, request_id VARCHAR(255) NOT NULL, created_at BIGINT NOT NULL, + project_type VARCHAR(255) NOT NULL DEFAULT '', PRIMARY KEY (queue, uri, project) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/stovepipe/extension/storage/mysql/validation_fact_store.go b/stovepipe/extension/storage/mysql/validation_fact_store.go index 6c58a5593..ce0ea7401 100644 --- a/stovepipe/extension/storage/mysql/validation_fact_store.go +++ b/stovepipe/extension/storage/mysql/validation_fact_store.go @@ -47,11 +47,12 @@ func (v *validationFactStore) Create(ctx context.Context, fact entity.Validation queue := v.queue _, err := v.db.ExecContext(ctx, - `INSERT INTO validation_fact (queue, uri, project, degree, request_id, created_at) - VALUES (?, ?, ?, ?, ?, ?)`, + `INSERT INTO validation_fact (queue, uri, project, project_type, degree, request_id, created_at) + VALUES (?, ?, ?, ?, ?, ?, ?)`, queue, fact.URI, fact.Project, + fact.ProjectType, fact.Degree, fact.RequestID, fact.CreatedAt, @@ -74,11 +75,12 @@ func (v *validationFactStore) Get(ctx context.Context, uri, project string) (ret queue := v.queue var fact entity.ValidationFact err := v.db.QueryRowContext(ctx, - "SELECT uri, project, degree, request_id, created_at FROM validation_fact WHERE queue = ? AND uri = ? AND project = ?", + "SELECT uri, project, project_type, degree, request_id, created_at FROM validation_fact WHERE queue = ? AND uri = ? AND project = ?", queue, uri, project, ).Scan( &fact.URI, &fact.Project, + &fact.ProjectType, &fact.Degree, &fact.RequestID, &fact.CreatedAt, @@ -102,7 +104,7 @@ func (v *validationFactStore) ListProjects(ctx context.Context, uri, afterProjec return nil, fmt.Errorf("validation fact list limit must be positive") } rows, err := v.db.QueryContext(ctx, ` - SELECT uri, project, degree, request_id, created_at FROM validation_fact + SELECT uri, project, project_type, degree, request_id, created_at FROM validation_fact WHERE queue = ? AND uri = ? AND project > ? ORDER BY project ASC LIMIT ?`, v.queue, uri, afterProject, limit, @@ -114,7 +116,7 @@ func (v *validationFactStore) ListProjects(ctx context.Context, uri, afterProjec for rows.Next() { var fact entity.ValidationFact - if err := rows.Scan(&fact.URI, &fact.Project, &fact.Degree, &fact.RequestID, &fact.CreatedAt); err != nil { + if err := rows.Scan(&fact.URI, &fact.Project, &fact.ProjectType, &fact.Degree, &fact.RequestID, &fact.CreatedAt); err != nil { return nil, fmt.Errorf("failed to scan project fact: %w", err) } ret = append(ret, fact) diff --git a/stovepipe/extension/storage/mysql/validation_fact_store_test.go b/stovepipe/extension/storage/mysql/validation_fact_store_test.go index cfb4c36e6..8b753efa5 100644 --- a/stovepipe/extension/storage/mysql/validation_fact_store_test.go +++ b/stovepipe/extension/storage/mysql/validation_fact_store_test.go @@ -47,11 +47,12 @@ func setupValidationFactStoreTest(t *testing.T) (*sql.DB, sqlmock.Sqlmock, stora func TestValidationFactStore_Create(t *testing.T) { fact := entity.ValidationFact{ - URI: testFactURI, - Project: "", - Degree: entity.DegreeGreen, - RequestID: "1", - CreatedAt: 1735689600000, + URI: testFactURI, + Project: "project-a", + ProjectType: "service", + Degree: entity.DegreeGreen, + RequestID: "1", + CreatedAt: 1735689600000, } tests := []struct { @@ -64,7 +65,7 @@ func TestValidationFactStore_Create(t *testing.T) { name: "success", setup: func(mock sqlmock.Sqlmock) { mock.ExpectExec("INSERT INTO validation_fact"). - WithArgs(testFactQueue, fact.URI, fact.Project, fact.Degree, fact.RequestID, fact.CreatedAt). + WithArgs(testFactQueue, fact.URI, fact.Project, fact.ProjectType, fact.Degree, fact.RequestID, fact.CreatedAt). WillReturnResult(sqlmock.NewResult(0, 1)) }, }, @@ -72,7 +73,7 @@ func TestValidationFactStore_Create(t *testing.T) { name: "duplicate identity returns ErrAlreadyExists", setup: func(mock sqlmock.Sqlmock) { mock.ExpectExec("INSERT INTO validation_fact"). - WithArgs(testFactQueue, fact.URI, fact.Project, fact.Degree, fact.RequestID, fact.CreatedAt). + WithArgs(testFactQueue, fact.URI, fact.Project, fact.ProjectType, fact.Degree, fact.RequestID, fact.CreatedAt). WillReturnError(&mysql.MySQLError{Number: mysqlErrDuplicateEntry}) }, wantErr: true, @@ -82,7 +83,7 @@ func TestValidationFactStore_Create(t *testing.T) { name: "other exec error", setup: func(mock sqlmock.Sqlmock) { mock.ExpectExec("INSERT INTO validation_fact"). - WithArgs(testFactQueue, fact.URI, fact.Project, fact.Degree, fact.RequestID, fact.CreatedAt). + WithArgs(testFactQueue, fact.URI, fact.Project, fact.ProjectType, fact.Degree, fact.RequestID, fact.CreatedAt). WillReturnError(fmt.Errorf("connection reset")) }, wantErr: true, @@ -112,11 +113,12 @@ func TestValidationFactStore_Create(t *testing.T) { func TestValidationFactStore_Get(t *testing.T) { want := entity.ValidationFact{ - URI: testFactURI, - Project: "", - Degree: entity.DegreeBroken, - RequestID: "2", - CreatedAt: 1735689600000, + URI: testFactURI, + Project: "project-a", + ProjectType: "service", + Degree: entity.DegreeBroken, + RequestID: "2", + CreatedAt: 1735689600000, } tests := []struct { @@ -129,9 +131,9 @@ func TestValidationFactStore_Get(t *testing.T) { { name: "found", setup: func(mock sqlmock.Sqlmock) { - rows := sqlmock.NewRows([]string{"uri", "project", "degree", "request_id", "created_at"}). - AddRow(want.URI, want.Project, want.Degree, want.RequestID, want.CreatedAt) - mock.ExpectQuery("SELECT uri, project, degree, request_id, created_at FROM validation_fact"). + rows := sqlmock.NewRows([]string{"uri", "project", "project_type", "degree", "request_id", "created_at"}). + AddRow(want.URI, want.Project, want.ProjectType, want.Degree, want.RequestID, want.CreatedAt) + mock.ExpectQuery("SELECT uri, project, project_type, degree, request_id, created_at FROM validation_fact"). WithArgs(testFactQueue, want.URI, want.Project). WillReturnRows(rows) }, @@ -140,7 +142,7 @@ func TestValidationFactStore_Get(t *testing.T) { { name: "not found", setup: func(mock sqlmock.Sqlmock) { - mock.ExpectQuery("SELECT uri, project, degree, request_id, created_at FROM validation_fact"). + mock.ExpectQuery("SELECT uri, project, project_type, degree, request_id, created_at FROM validation_fact"). WithArgs(testFactQueue, want.URI, want.Project). WillReturnError(sql.ErrNoRows) }, @@ -150,7 +152,7 @@ func TestValidationFactStore_Get(t *testing.T) { { name: "query error", setup: func(mock sqlmock.Sqlmock) { - mock.ExpectQuery("SELECT uri, project, degree, request_id, created_at FROM validation_fact"). + mock.ExpectQuery("SELECT uri, project, project_type, degree, request_id, created_at FROM validation_fact"). WithArgs(testFactQueue, want.URI, want.Project). WillReturnError(fmt.Errorf("connection reset")) }, @@ -182,8 +184,8 @@ func TestValidationFactStore_Get(t *testing.T) { func TestValidationFactStore_ListProjects(t *testing.T) { readErr := fmt.Errorf("connection reset") - columns := []string{"uri", "project", "degree", "request_id", "created_at"} - want := entity.ValidationFact{URI: testFactURI, Project: "project-b", Degree: 0.5, RequestID: "2", CreatedAt: 20} + columns := []string{"uri", "project", "project_type", "degree", "request_id", "created_at"} + want := entity.ValidationFact{URI: testFactURI, Project: "project-b", ProjectType: "future-type", Degree: 0.5, RequestID: "2", CreatedAt: 20} tests := []struct { name string rows *sqlmock.Rows @@ -191,17 +193,17 @@ func TestValidationFactStore_ListProjects(t *testing.T) { wantErr bool want []entity.ValidationFact }{ - {name: "found", rows: sqlmock.NewRows(columns).AddRow(want.URI, want.Project, want.Degree, want.RequestID, want.CreatedAt), want: []entity.ValidationFact{want}}, + {name: "found", rows: sqlmock.NewRows(columns).AddRow(want.URI, want.Project, want.ProjectType, want.Degree, want.RequestID, want.CreatedAt), want: []entity.ValidationFact{want}}, {name: "empty", rows: sqlmock.NewRows(columns)}, {name: "query failure", queryErr: readErr, wantErr: true}, - {name: "scan failure", rows: sqlmock.NewRows(columns).AddRow(want.URI, want.Project, "invalid", want.RequestID, want.CreatedAt), wantErr: true}, - {name: "iteration failure", rows: sqlmock.NewRows(columns).AddRow(want.URI, want.Project, want.Degree, want.RequestID, want.CreatedAt).RowError(0, readErr), wantErr: true}, + {name: "scan failure", rows: sqlmock.NewRows(columns).AddRow(want.URI, want.Project, want.ProjectType, "invalid", want.RequestID, want.CreatedAt), wantErr: true}, + {name: "iteration failure", rows: sqlmock.NewRows(columns).AddRow(want.URI, want.Project, want.ProjectType, want.Degree, want.RequestID, want.CreatedAt).RowError(0, readErr), wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { db, mock, store := setupValidationFactStoreTest(t) defer db.Close() - query := mock.ExpectQuery(`SELECT uri, project, degree, request_id, created_at FROM validation_fact\s+WHERE queue = \? AND uri = \? AND project > \?\s+ORDER BY project ASC LIMIT \?`).WithArgs(testFactQueue, testFactURI, "project-a", 3) + query := mock.ExpectQuery(`SELECT uri, project, project_type, degree, request_id, created_at FROM validation_fact\s+WHERE queue = \? AND uri = \? AND project > \?\s+ORDER BY project ASC LIMIT \?`).WithArgs(testFactQueue, testFactURI, "project-a", 3) if tt.queryErr != nil { query.WillReturnError(tt.queryErr) } else { diff --git a/test/integration/stovepipe/extension/storage/suite.go b/test/integration/stovepipe/extension/storage/suite.go index fec3a3d8b..f2588cbc8 100644 --- a/test/integration/stovepipe/extension/storage/suite.go +++ b/test/integration/stovepipe/extension/storage/suite.go @@ -686,9 +686,9 @@ func (s *ValidationFactStoreContractSuite) TestValidationFactStore_ListProjects( const queue = "contract/fact-list-projects" const uri = "git://remote/contract/fact-list-projects/aaaa" store := s.storeFor(queue) - failedA := entity.ValidationFact{URI: uri, Project: "a", Degree: 0.25, RequestID: "1", CreatedAt: 10} + failedA := entity.ValidationFact{URI: uri, Project: "a", ProjectType: "future-type", Degree: 0.25, RequestID: "1", CreatedAt: 10} failedC := entity.ValidationFact{URI: uri, Project: "c", Degree: 1, RequestID: "1", CreatedAt: 20} - greenB := entity.ValidationFact{URI: uri, Project: "b", Degree: 0, RequestID: "1", CreatedAt: 30} + greenB := entity.ValidationFact{URI: uri, Project: "b", ProjectType: "service", Degree: 0, RequestID: "1", CreatedAt: 30} for _, fact := range []entity.ValidationFact{ failedC, failedA, greenB, diff --git a/web/api/src/gen/api/stovepipe/proto/stovepipe_pb.ts b/web/api/src/gen/api/stovepipe/proto/stovepipe_pb.ts index 6e423cddd..9e306877e 100644 --- a/web/api/src/gen/api/stovepipe/proto/stovepipe_pb.ts +++ b/web/api/src/gen/api/stovepipe/proto/stovepipe_pb.ts @@ -24,7 +24,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file api/stovepipe/proto/stovepipe.proto. */ export const file_api_stovepipe_proto_stovepipe: GenFile = /*@__PURE__*/ - fileDesc("CiNhcGkvc3RvdmVwaXBlL3Byb3RvL3N0b3ZlcGlwZS5wcm90bxIadWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUiHgoLUGluZ1JlcXVlc3QSDwoHbWVzc2FnZRgBIAEoCSJaCgxQaW5nUmVzcG9uc2USDwoHbWVzc2FnZRgBIAEoCRIUCgxzZXJ2aWNlX25hbWUYAiABKAkSEQoJdGltZXN0YW1wGAMgASgDEhAKCGhvc3RuYW1lGAQgASgJIl8KDUluZ2VzdFJlcXVlc3QSDQoFcXVldWUYASABKAkSPwoKZW5hYmxlbWVudBgCIAEoDjIrLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlF1ZXVlRW5hYmxlbWVudCJjCg5Jbmdlc3RSZXNwb25zZRIKCgJpZBgBIAEoCRJFCgplbmFibGVtZW50GAIgASgLMjEudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuUXVldWVFbmFibGVtZW50U3RhdHVzIkEKHEdldFJlcXVlc3RIaXN0b3J5QnlJRFJlcXVlc3QSDQoFcXVldWUYASABKAkSEgoKcmVxdWVzdF9pZBgCIAEoCSI7Ch1HZXRSZXF1ZXN0SGlzdG9yeUJ5VVJJUmVxdWVzdBINCgVxdWV1ZRgBIAEoCRILCgN1cmkYAiABKAkiwgEKDEhpc3RvcnlFdmVudBIQCghldmVudF9pZBgBIAEoCRIUCgx0aW1lc3RhbXBfbXMYAiABKAMSFwoNcmVxdWVzdF9zdGF0ZRgDIAEoCUgAEg8KBWV2ZW50GAQgASgJSAASFgoOb3V0Y29tZV9yZWFzb24YBSABKAkSOgoEZGF0YRgGIAEoCzIsLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkhpc3RvcnlFdmVudERhdGFCDAoKb2NjdXJyZW5jZSKABAoQSGlzdG9yeUV2ZW50RGF0YRJMCg1yZXF1ZXN0X3N0YXRlGAEgASgLMjMudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuSGlzdG9yeVJlcXVlc3RTdGF0ZURhdGFIABJHCg9idWlsZF90cmlnZ2VyZWQYAiABKAsyLC51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5IaXN0b3J5QnVpbGREYXRhSAASRgoOYnVpbGRfZmluaXNoZWQYAyABKAsyLC51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5IaXN0b3J5QnVpbGREYXRhSAASWQoYdmFsaWRhdGlvbl9mYWN0X3JlY29yZGVkGAQgASgLMjUudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuSGlzdG9yeVZhbGlkYXRpb25GYWN0RGF0YUgAElUKFnByb2plY3RfZmFjdHNfcmVjb3JkZWQYBSABKAsyMy51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5IaXN0b3J5UHJvamVjdEZhY3RzRGF0YUgAElAKGGJ1aWxkX2FuYWx5c2lzX2FiYW5kb25lZBgGIAEoCzIsLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkhpc3RvcnlCdWlsZERhdGFIAEIJCgdwYXlsb2FkIisKF0hpc3RvcnlSZXF1ZXN0U3RhdGVEYXRhEhAKCGJ1aWxkX2lkGAEgASgJIiQKEEhpc3RvcnlCdWlsZERhdGESEAoIYnVpbGRfaWQYASABKAkiMAoZSGlzdG9yeVZhbGlkYXRpb25GYWN0RGF0YRITCgtmYWN0X2RlZ3JlZRgBIAEoASI1ChdIaXN0b3J5UHJvamVjdEZhY3RzRGF0YRIaChJwcm9qZWN0X2ZhY3RfY291bnQYASABKAQiowEKDlJlcXVlc3RIaXN0b3J5EhIKCnJlcXVlc3RfaWQYASABKAkSOAoGZXZlbnRzGAIgAygLMigudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuSGlzdG9yeUV2ZW50EkMKD3JlcXVlc3Rfc3VtbWFyeRgDIAEoCzIqLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlJlcXVlc3RTdW1tYXJ5Ip4BCh1HZXRSZXF1ZXN0SGlzdG9yeUJ5SURSZXNwb25zZRI4CgZldmVudHMYASADKAsyKC51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5IaXN0b3J5RXZlbnQSQwoPcmVxdWVzdF9zdW1tYXJ5GAIgASgLMioudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuUmVxdWVzdFN1bW1hcnkiXwoeR2V0UmVxdWVzdEhpc3RvcnlCeVVSSVJlc3BvbnNlEj0KCWhpc3RvcmllcxgBIAMoCzIqLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlJlcXVlc3RIaXN0b3J5InkKG0dldFByb2plY3RTdGF0dXNCeUlEUmVxdWVzdBINCgVxdWV1ZRgBIAEoCRISCgpyZXF1ZXN0X2lkGAIgASgJEhAKCHByb2plY3RzGAMgAygJEhEKCXBhZ2Vfc2l6ZRgEIAEoBRISCgpwYWdlX3Rva2VuGAUgASgJInoKHEdldFByb2plY3RTdGF0dXNCeVVSSVJlcXVlc3QSDQoFcXVldWUYASABKAkSEgoKY2hhbmdlX3VyaRgCIAEoCRIQCghwcm9qZWN0cxgDIAMoCRIRCglwYWdlX3NpemUYBCABKAUSEgoKcGFnZV90b2tlbhgFIAEoCSJJChFQcm9qZWN0VmFsaWRhdGlvbhIPCgdwcm9qZWN0GAEgASgJEhkKD2JyZWFrYWdlX2RlZ3JlZRgCIAEoAUgAQggKBnJlc3VsdCLMAgocR2V0UHJvamVjdFN0YXR1c0J5SURSZXNwb25zZRISCgpyZXF1ZXN0X2lkGAEgASgJEg0KBXF1ZXVlGAIgASgJEhIKCmNoYW5nZV91cmkYAyABKAkSEAoIYmFzZV91cmkYBCABKAkSFQoNcmVxdWVzdF9zdGF0ZRgFIAEoCRIVCg11cGRhdGVkX2F0X21zGAogASgDEiQKGnJlcG9zaXRvcnlfYnJlYWthZ2VfZGVncmVlGAYgASgBSAASIAoYcHJvamVjdF9yZXN1bHRzX2NvbXBsZXRlGAcgASgIEj8KCHByb2plY3RzGAggAygLMi0udWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuUHJvamVjdFZhbGlkYXRpb24SFwoPbmV4dF9wYWdlX3Rva2VuGAkgASgJQhMKEXJlcG9zaXRvcnlfcmVzdWx0Is0CCh1HZXRQcm9qZWN0U3RhdHVzQnlVUklSZXNwb25zZRISCgpyZXF1ZXN0X2lkGAEgASgJEg0KBXF1ZXVlGAIgASgJEhIKCmNoYW5nZV91cmkYAyABKAkSEAoIYmFzZV91cmkYBCABKAkSFQoNcmVxdWVzdF9zdGF0ZRgFIAEoCRIVCg11cGRhdGVkX2F0X21zGAogASgDEiQKGnJlcG9zaXRvcnlfYnJlYWthZ2VfZGVncmVlGAYgASgBSAASIAoYcHJvamVjdF9yZXN1bHRzX2NvbXBsZXRlGAcgASgIEj8KCHByb2plY3RzGAggAygLMi0udWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuUHJvamVjdFZhbGlkYXRpb24SFwoPbmV4dF9wYWdlX3Rva2VuGAkgASgJQhMKEXJlcG9zaXRvcnlfcmVzdWx0IvIBCg5SZXF1ZXN0U3VtbWFyeRISCgpyZXF1ZXN0X2lkGAEgASgJEg0KBXF1ZXVlGAIgASgJEhIKCmNoYW5nZV91cmkYAyABKAkSEAoIYmFzZV91cmkYBCABKAkSFQoNcmVxdWVzdF9zdGF0ZRgFIAEoCRIbChNzdGF0ZV91cGRhdGVkX2F0X21zGAYgASgDEhYKDmFjY2VwdGVkX2F0X21zGAcgASgDEhYKDm91dGNvbWVfcmVhc29uGAggASgJEhUKDXByb2plY3RfY291bnQYCSABKAUSHAoUYnJva2VuX3Byb2plY3RfY291bnQYCiABKAUitAEKC0xpc3RSZXF1ZXN0Eg0KBXF1ZXVlGAEgASgJEiEKF2FjY2VwdGVkX2F0X29yX2FmdGVyX21zGAIgASgDSAASHAoSYWNjZXB0ZWRfYmVmb3JlX21zGAMgASgDSAESEQoJcGFnZV9zaXplGAQgASgFEhIKCnBhZ2VfdG9rZW4YBSABKAlCFgoUYWNjZXB0ZWRfbG93ZXJfYm91bmRCFgoUYWNjZXB0ZWRfdXBwZXJfYm91bmQiZQoMTGlzdFJlc3BvbnNlEjwKCHJlcXVlc3RzGAEgAygLMioudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuUmVxdWVzdFN1bW1hcnkSFwoPbmV4dF9wYWdlX3Rva2VuGAIgASgJIjoKFUdldFF1ZXVlU3RhdHVzUmVxdWVzdBINCgVxdWV1ZRgBIAEoCRISCgpjaGFuZ2VfdXJpGAIgASgJIp4BChVRdWV1ZUVuYWJsZW1lbnRTdGF0dXMSDwoHdmVyc2lvbhgBIAEoAxI6CgVzdGF0ZRgCIAEoDjIrLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlF1ZXVlRW5hYmxlbWVudBIhChllZmZlY3RpdmVfZnJvbV9jb21taXRfdXJpGAMgASgJEhUKDWNoYW5nZWRfYXRfbXMYBCABKAMibgoWR2V0UXVldWVTdGF0dXNSZXNwb25zZRINCgVxdWV1ZRgBIAEoCRJFCgplbmFibGVtZW50GAIgASgLMjEudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuUXVldWVFbmFibGVtZW50U3RhdHVzIpoBCgxJc3N1ZVN1bW1hcnkSDwoHdGFza19pZBgBIAEoCRIPCgd1bml0X2lkGAIgASgJEg4KBnN0YXR1cxgDIAEoCRIbChNmaXJzdF9zZWVuX2J1aWxkX2lkGAQgASgJEhIKCnJlcXVlc3RfaWQYBSABKAkSEAoIYnVpbGRfaWQYBiABKAkSFQoNY2hhbmdlZF9hdF9tcxgHIAEoAyJNChVMaXN0T3Blbklzc3Vlc1JlcXVlc3QSDQoFcXVldWUYASABKAkSEQoJcGFnZV9zaXplGAIgASgFEhIKCnBhZ2VfdG9rZW4YAyABKAkiawoWTGlzdE9wZW5Jc3N1ZXNSZXNwb25zZRI4CgZpc3N1ZXMYASADKAsyKC51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5Jc3N1ZVN1bW1hcnkSFwoPbmV4dF9wYWdlX3Rva2VuGAIgASgJKmwKD1F1ZXVlRW5hYmxlbWVudBIcChhRVUVVRV9FTkFCTEVNRU5UX1VOS05PV04QABIcChhRVUVVRV9FTkFCTEVNRU5UX0VOQUJMRUQQARIdChlRVUVVRV9FTkFCTEVNRU5UX0RJU0FCTEVEEAIy4ggKCVN0b3ZlcGlwZRJbCgRQaW5nEicudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuUGluZ1JlcXVlc3QaKC51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5QaW5nUmVzcG9uc2UiABJhCgZJbmdlc3QSKS51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5Jbmdlc3RSZXF1ZXN0GioudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuSW5nZXN0UmVzcG9uc2UiABKOAQoVR2V0UmVxdWVzdEhpc3RvcnlCeUlEEjgudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuR2V0UmVxdWVzdEhpc3RvcnlCeUlEUmVxdWVzdBo5LnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkdldFJlcXVlc3RIaXN0b3J5QnlJRFJlc3BvbnNlIgASkQEKFkdldFJlcXVlc3RIaXN0b3J5QnlVUkkSOS51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5HZXRSZXF1ZXN0SGlzdG9yeUJ5VVJJUmVxdWVzdBo6LnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkdldFJlcXVlc3RIaXN0b3J5QnlVUklSZXNwb25zZSIAEosBChRHZXRQcm9qZWN0U3RhdHVzQnlJRBI3LnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkdldFByb2plY3RTdGF0dXNCeUlEUmVxdWVzdBo4LnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkdldFByb2plY3RTdGF0dXNCeUlEUmVzcG9uc2UiABKOAQoVR2V0UHJvamVjdFN0YXR1c0J5VVJJEjgudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuR2V0UHJvamVjdFN0YXR1c0J5VVJJUmVxdWVzdBo5LnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkdldFByb2plY3RTdGF0dXNCeVVSSVJlc3BvbnNlIgASWwoETGlzdBInLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkxpc3RSZXF1ZXN0GigudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuTGlzdFJlc3BvbnNlIgASeQoOR2V0UXVldWVTdGF0dXMSMS51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5HZXRRdWV1ZVN0YXR1c1JlcXVlc3QaMi51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5HZXRRdWV1ZVN0YXR1c1Jlc3BvbnNlIgASeQoOTGlzdE9wZW5Jc3N1ZXMSMS51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5MaXN0T3Blbklzc3Vlc1JlcXVlc3QaMi51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5MaXN0T3Blbklzc3Vlc1Jlc3BvbnNlIgBCZQoeY29tLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlQg5TdG92ZXBpcGVQcm90b1ABWjFnaXRodWIuY29tL3ViZXIvc3VibWl0cXVldWUvYXBpL3N0b3ZlcGlwZS9wcm90b3BiYgZwcm90bzM"); + fileDesc("CiNhcGkvc3RvdmVwaXBlL3Byb3RvL3N0b3ZlcGlwZS5wcm90bxIadWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUiHgoLUGluZ1JlcXVlc3QSDwoHbWVzc2FnZRgBIAEoCSJaCgxQaW5nUmVzcG9uc2USDwoHbWVzc2FnZRgBIAEoCRIUCgxzZXJ2aWNlX25hbWUYAiABKAkSEQoJdGltZXN0YW1wGAMgASgDEhAKCGhvc3RuYW1lGAQgASgJIl8KDUluZ2VzdFJlcXVlc3QSDQoFcXVldWUYASABKAkSPwoKZW5hYmxlbWVudBgCIAEoDjIrLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlF1ZXVlRW5hYmxlbWVudCJjCg5Jbmdlc3RSZXNwb25zZRIKCgJpZBgBIAEoCRJFCgplbmFibGVtZW50GAIgASgLMjEudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuUXVldWVFbmFibGVtZW50U3RhdHVzIkEKHEdldFJlcXVlc3RIaXN0b3J5QnlJRFJlcXVlc3QSDQoFcXVldWUYASABKAkSEgoKcmVxdWVzdF9pZBgCIAEoCSI7Ch1HZXRSZXF1ZXN0SGlzdG9yeUJ5VVJJUmVxdWVzdBINCgVxdWV1ZRgBIAEoCRILCgN1cmkYAiABKAkiwgEKDEhpc3RvcnlFdmVudBIQCghldmVudF9pZBgBIAEoCRIUCgx0aW1lc3RhbXBfbXMYAiABKAMSFwoNcmVxdWVzdF9zdGF0ZRgDIAEoCUgAEg8KBWV2ZW50GAQgASgJSAASFgoOb3V0Y29tZV9yZWFzb24YBSABKAkSOgoEZGF0YRgGIAEoCzIsLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkhpc3RvcnlFdmVudERhdGFCDAoKb2NjdXJyZW5jZSKABAoQSGlzdG9yeUV2ZW50RGF0YRJMCg1yZXF1ZXN0X3N0YXRlGAEgASgLMjMudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuSGlzdG9yeVJlcXVlc3RTdGF0ZURhdGFIABJHCg9idWlsZF90cmlnZ2VyZWQYAiABKAsyLC51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5IaXN0b3J5QnVpbGREYXRhSAASRgoOYnVpbGRfZmluaXNoZWQYAyABKAsyLC51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5IaXN0b3J5QnVpbGREYXRhSAASWQoYdmFsaWRhdGlvbl9mYWN0X3JlY29yZGVkGAQgASgLMjUudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuSGlzdG9yeVZhbGlkYXRpb25GYWN0RGF0YUgAElUKFnByb2plY3RfZmFjdHNfcmVjb3JkZWQYBSABKAsyMy51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5IaXN0b3J5UHJvamVjdEZhY3RzRGF0YUgAElAKGGJ1aWxkX2FuYWx5c2lzX2FiYW5kb25lZBgGIAEoCzIsLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkhpc3RvcnlCdWlsZERhdGFIAEIJCgdwYXlsb2FkIisKF0hpc3RvcnlSZXF1ZXN0U3RhdGVEYXRhEhAKCGJ1aWxkX2lkGAEgASgJIiQKEEhpc3RvcnlCdWlsZERhdGESEAoIYnVpbGRfaWQYASABKAkiMAoZSGlzdG9yeVZhbGlkYXRpb25GYWN0RGF0YRITCgtmYWN0X2RlZ3JlZRgBIAEoASI1ChdIaXN0b3J5UHJvamVjdEZhY3RzRGF0YRIaChJwcm9qZWN0X2ZhY3RfY291bnQYASABKAQiowEKDlJlcXVlc3RIaXN0b3J5EhIKCnJlcXVlc3RfaWQYASABKAkSOAoGZXZlbnRzGAIgAygLMigudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuSGlzdG9yeUV2ZW50EkMKD3JlcXVlc3Rfc3VtbWFyeRgDIAEoCzIqLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlJlcXVlc3RTdW1tYXJ5Ip4BCh1HZXRSZXF1ZXN0SGlzdG9yeUJ5SURSZXNwb25zZRI4CgZldmVudHMYASADKAsyKC51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5IaXN0b3J5RXZlbnQSQwoPcmVxdWVzdF9zdW1tYXJ5GAIgASgLMioudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuUmVxdWVzdFN1bW1hcnkiXwoeR2V0UmVxdWVzdEhpc3RvcnlCeVVSSVJlc3BvbnNlEj0KCWhpc3RvcmllcxgBIAMoCzIqLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlJlcXVlc3RIaXN0b3J5InkKG0dldFByb2plY3RTdGF0dXNCeUlEUmVxdWVzdBINCgVxdWV1ZRgBIAEoCRISCgpyZXF1ZXN0X2lkGAIgASgJEhAKCHByb2plY3RzGAMgAygJEhEKCXBhZ2Vfc2l6ZRgEIAEoBRISCgpwYWdlX3Rva2VuGAUgASgJInoKHEdldFByb2plY3RTdGF0dXNCeVVSSVJlcXVlc3QSDQoFcXVldWUYASABKAkSEgoKY2hhbmdlX3VyaRgCIAEoCRIQCghwcm9qZWN0cxgDIAMoCRIRCglwYWdlX3NpemUYBCABKAUSEgoKcGFnZV90b2tlbhgFIAEoCSJfChFQcm9qZWN0VmFsaWRhdGlvbhIPCgdwcm9qZWN0GAEgASgJEhkKD2JyZWFrYWdlX2RlZ3JlZRgCIAEoAUgAEhQKDHByb2plY3RfdHlwZRgDIAEoCUIICgZyZXN1bHQizAIKHEdldFByb2plY3RTdGF0dXNCeUlEUmVzcG9uc2USEgoKcmVxdWVzdF9pZBgBIAEoCRINCgVxdWV1ZRgCIAEoCRISCgpjaGFuZ2VfdXJpGAMgASgJEhAKCGJhc2VfdXJpGAQgASgJEhUKDXJlcXVlc3Rfc3RhdGUYBSABKAkSFQoNdXBkYXRlZF9hdF9tcxgKIAEoAxIkChpyZXBvc2l0b3J5X2JyZWFrYWdlX2RlZ3JlZRgGIAEoAUgAEiAKGHByb2plY3RfcmVzdWx0c19jb21wbGV0ZRgHIAEoCBI/Cghwcm9qZWN0cxgIIAMoCzItLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlByb2plY3RWYWxpZGF0aW9uEhcKD25leHRfcGFnZV90b2tlbhgJIAEoCUITChFyZXBvc2l0b3J5X3Jlc3VsdCLNAgodR2V0UHJvamVjdFN0YXR1c0J5VVJJUmVzcG9uc2USEgoKcmVxdWVzdF9pZBgBIAEoCRINCgVxdWV1ZRgCIAEoCRISCgpjaGFuZ2VfdXJpGAMgASgJEhAKCGJhc2VfdXJpGAQgASgJEhUKDXJlcXVlc3Rfc3RhdGUYBSABKAkSFQoNdXBkYXRlZF9hdF9tcxgKIAEoAxIkChpyZXBvc2l0b3J5X2JyZWFrYWdlX2RlZ3JlZRgGIAEoAUgAEiAKGHByb2plY3RfcmVzdWx0c19jb21wbGV0ZRgHIAEoCBI/Cghwcm9qZWN0cxgIIAMoCzItLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlByb2plY3RWYWxpZGF0aW9uEhcKD25leHRfcGFnZV90b2tlbhgJIAEoCUITChFyZXBvc2l0b3J5X3Jlc3VsdCLyAQoOUmVxdWVzdFN1bW1hcnkSEgoKcmVxdWVzdF9pZBgBIAEoCRINCgVxdWV1ZRgCIAEoCRISCgpjaGFuZ2VfdXJpGAMgASgJEhAKCGJhc2VfdXJpGAQgASgJEhUKDXJlcXVlc3Rfc3RhdGUYBSABKAkSGwoTc3RhdGVfdXBkYXRlZF9hdF9tcxgGIAEoAxIWCg5hY2NlcHRlZF9hdF9tcxgHIAEoAxIWCg5vdXRjb21lX3JlYXNvbhgIIAEoCRIVCg1wcm9qZWN0X2NvdW50GAkgASgFEhwKFGJyb2tlbl9wcm9qZWN0X2NvdW50GAogASgFIrQBCgtMaXN0UmVxdWVzdBINCgVxdWV1ZRgBIAEoCRIhChdhY2NlcHRlZF9hdF9vcl9hZnRlcl9tcxgCIAEoA0gAEhwKEmFjY2VwdGVkX2JlZm9yZV9tcxgDIAEoA0gBEhEKCXBhZ2Vfc2l6ZRgEIAEoBRISCgpwYWdlX3Rva2VuGAUgASgJQhYKFGFjY2VwdGVkX2xvd2VyX2JvdW5kQhYKFGFjY2VwdGVkX3VwcGVyX2JvdW5kImUKDExpc3RSZXNwb25zZRI8CghyZXF1ZXN0cxgBIAMoCzIqLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlJlcXVlc3RTdW1tYXJ5EhcKD25leHRfcGFnZV90b2tlbhgCIAEoCSI6ChVHZXRRdWV1ZVN0YXR1c1JlcXVlc3QSDQoFcXVldWUYASABKAkSEgoKY2hhbmdlX3VyaRgCIAEoCSKeAQoVUXVldWVFbmFibGVtZW50U3RhdHVzEg8KB3ZlcnNpb24YASABKAMSOgoFc3RhdGUYAiABKA4yKy51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5RdWV1ZUVuYWJsZW1lbnQSIQoZZWZmZWN0aXZlX2Zyb21fY29tbWl0X3VyaRgDIAEoCRIVCg1jaGFuZ2VkX2F0X21zGAQgASgDIm4KFkdldFF1ZXVlU3RhdHVzUmVzcG9uc2USDQoFcXVldWUYASABKAkSRQoKZW5hYmxlbWVudBgCIAEoCzIxLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlF1ZXVlRW5hYmxlbWVudFN0YXR1cyKaAQoMSXNzdWVTdW1tYXJ5Eg8KB3Rhc2tfaWQYASABKAkSDwoHdW5pdF9pZBgCIAEoCRIOCgZzdGF0dXMYAyABKAkSGwoTZmlyc3Rfc2Vlbl9idWlsZF9pZBgEIAEoCRISCgpyZXF1ZXN0X2lkGAUgASgJEhAKCGJ1aWxkX2lkGAYgASgJEhUKDWNoYW5nZWRfYXRfbXMYByABKAMiTQoVTGlzdE9wZW5Jc3N1ZXNSZXF1ZXN0Eg0KBXF1ZXVlGAEgASgJEhEKCXBhZ2Vfc2l6ZRgCIAEoBRISCgpwYWdlX3Rva2VuGAMgASgJImsKFkxpc3RPcGVuSXNzdWVzUmVzcG9uc2USOAoGaXNzdWVzGAEgAygLMigudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuSXNzdWVTdW1tYXJ5EhcKD25leHRfcGFnZV90b2tlbhgCIAEoCSpsCg9RdWV1ZUVuYWJsZW1lbnQSHAoYUVVFVUVfRU5BQkxFTUVOVF9VTktOT1dOEAASHAoYUVVFVUVfRU5BQkxFTUVOVF9FTkFCTEVEEAESHQoZUVVFVUVfRU5BQkxFTUVOVF9ESVNBQkxFRBACMuIICglTdG92ZXBpcGUSWwoEUGluZxInLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLlBpbmdSZXF1ZXN0GigudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuUGluZ1Jlc3BvbnNlIgASYQoGSW5nZXN0EikudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuSW5nZXN0UmVxdWVzdBoqLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkluZ2VzdFJlc3BvbnNlIgASjgEKFUdldFJlcXVlc3RIaXN0b3J5QnlJRBI4LnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkdldFJlcXVlc3RIaXN0b3J5QnlJRFJlcXVlc3QaOS51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5HZXRSZXF1ZXN0SGlzdG9yeUJ5SURSZXNwb25zZSIAEpEBChZHZXRSZXF1ZXN0SGlzdG9yeUJ5VVJJEjkudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuR2V0UmVxdWVzdEhpc3RvcnlCeVVSSVJlcXVlc3QaOi51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5HZXRSZXF1ZXN0SGlzdG9yeUJ5VVJJUmVzcG9uc2UiABKLAQoUR2V0UHJvamVjdFN0YXR1c0J5SUQSNy51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5HZXRQcm9qZWN0U3RhdHVzQnlJRFJlcXVlc3QaOC51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5HZXRQcm9qZWN0U3RhdHVzQnlJRFJlc3BvbnNlIgASjgEKFUdldFByb2plY3RTdGF0dXNCeVVSSRI4LnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkdldFByb2plY3RTdGF0dXNCeVVSSVJlcXVlc3QaOS51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5HZXRQcm9qZWN0U3RhdHVzQnlVUklSZXNwb25zZSIAElsKBExpc3QSJy51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZS5MaXN0UmVxdWVzdBooLnViZXIuc3VibWl0cXVldWUuc3RvdmVwaXBlLkxpc3RSZXNwb25zZSIAEnkKDkdldFF1ZXVlU3RhdHVzEjEudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuR2V0UXVldWVTdGF0dXNSZXF1ZXN0GjIudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuR2V0UXVldWVTdGF0dXNSZXNwb25zZSIAEnkKDkxpc3RPcGVuSXNzdWVzEjEudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuTGlzdE9wZW5Jc3N1ZXNSZXF1ZXN0GjIudWJlci5zdWJtaXRxdWV1ZS5zdG92ZXBpcGUuTGlzdE9wZW5Jc3N1ZXNSZXNwb25zZSIAQmUKHmNvbS51YmVyLnN1Ym1pdHF1ZXVlLnN0b3ZlcGlwZUIOU3RvdmVwaXBlUHJvdG9QAVoxZ2l0aHViLmNvbS91YmVyL3N1Ym1pdHF1ZXVlL2FwaS9zdG92ZXBpcGUvcHJvdG9wYmIGcHJvdG8z"); /** * PingRequest is the request for the Ping method @@ -629,6 +629,13 @@ export type ProjectValidation = Message<"uber.submitqueue.stovepipe.ProjectValid value: number; case: "breakageDegree"; } | { case: undefined; value?: undefined }; + + /** + * Provider-defined project classification; empty means unknown. + * + * @generated from field: string project_type = 3; + */ + projectType: string; }; /** diff --git a/web/stovepipe/controller/projects.ts b/web/stovepipe/controller/projects.ts index d28fdab0b..eede62e8f 100644 --- a/web/stovepipe/controller/projects.ts +++ b/web/stovepipe/controller/projects.ts @@ -34,6 +34,7 @@ export async function loadProjectStatus( })), projects: result.projects.map((entry) => ({ project: entry.project, + projectType: entry.projectType, breakageDegree: entry.result.case === "breakageDegree" ? breakageDegree(entry.result.value) : null, })), diff --git a/web/stovepipe/entity/index.ts b/web/stovepipe/entity/index.ts index a0185391e..a30110a71 100644 --- a/web/stovepipe/entity/index.ts +++ b/web/stovepipe/entity/index.ts @@ -81,7 +81,7 @@ export interface ProjectStatusModel { /** Supported page sizes and their first-page URLs for this request. */ pageSizeChoices: { value: number; href: string }[]; /** Recorded projects on this page; null breakage means no result. */ - projects: { project: string; breakageDegree: number | null }[]; + projects: { project: string; projectType: string; breakageDegree: number | null }[]; /** First project page for this request; null on the first page. */ firstHref: string | null; /** Opaque project continuation URL; null on the final page. */ diff --git a/web/stovepipe/extension/service/index.ts b/web/stovepipe/extension/service/index.ts index c3e069fea..d22faa3d0 100644 --- a/web/stovepipe/extension/service/index.ts +++ b/web/stovepipe/extension/service/index.ts @@ -30,6 +30,7 @@ export interface ProjectStatus { projectResultsComplete: boolean; projects: { project: string; + projectType: string; result: | { case: "breakageDegree"; value: number } | { case: undefined; value?: undefined }; diff --git a/web/stovepipe/module.test.ts b/web/stovepipe/module.test.ts index 0812fd1a8..00f5030e9 100644 --- a/web/stovepipe/module.test.ts +++ b/web/stovepipe/module.test.ts @@ -43,9 +43,9 @@ function createPageTestContext() { repositoryResult: { case: "repositoryBreakageDegree", value: 0 }, projectResultsComplete: false, projects: [ - { project: "green", result: { case: "breakageDegree", value: 0 } }, - { project: "broken", result: { case: "breakageDegree", value: 0.25 } }, - { project: "unknown", result: { case: undefined } }, + { project: "green", projectType: "service", result: { case: "breakageDegree", value: 0 } }, + { project: "broken", projectType: "future-type", result: { case: "breakageDegree", value: 0.25 } }, + { project: "unknown", projectType: "", result: { case: undefined } }, ], nextPageToken: "next+/=", }), @@ -359,9 +359,9 @@ describe("Stovepipe page loading", () => { repositoryBreakageDegree: 0, complete: false, projects: [ - { project: "green", breakageDegree: 0 }, - { project: "broken", breakageDegree: 0.25 }, - { project: "unknown", breakageDegree: null }, + { project: "green", projectType: "service", breakageDegree: 0 }, + { project: "broken", projectType: "future-type", breakageDegree: 0.25 }, + { project: "unknown", projectType: "", breakageDegree: null }, ], firstHref: paths.request(summary.queue, summary.requestId), nextHref: paths.request(summary.queue, summary.requestId, "next+/="), @@ -404,7 +404,7 @@ describe("Stovepipe page loading", () => { it.each([ { queue: "other" }, { requestId: "other" }, - { projects: [{ project: "invalid", result: { case: "breakageDegree", value: NaN } }] }, + { projects: [{ project: "invalid", projectType: "", result: { case: "breakageDegree", value: NaN } }] }, { repositoryResult: { case: "repositoryBreakageDegree", value: 1.1 } }, ] satisfies Partial[])("rejects mismatched or malformed project status %j", async (overrides) => { const { app, service, paths } = createPageTestContext(); diff --git a/web/stovepipe/view/app.test.tsx b/web/stovepipe/view/app.test.tsx index 4c2df04f4..286e64233 100644 --- a/web/stovepipe/view/app.test.tsx +++ b/web/stovepipe/view/app.test.tsx @@ -25,10 +25,10 @@ const model: StovepipePageModel = { value, href: "/repo%2Fmain/request/7?projectPageSize=" + value, })), projects: [ - { project: "green-project", breakageDegree: 0 }, - { project: "partial-breakage-project", breakageDegree: 0.25 }, - { project: "broken-project", breakageDegree: 1 }, - { project: "unknown-project", breakageDegree: null }, + { project: "green-project", projectType: "service", breakageDegree: 0 }, + { project: "partial-breakage-project", projectType: "future-type", breakageDegree: 0.25 }, + { project: "broken-project", projectType: "", breakageDegree: 1 }, + { project: "unknown-project", projectType: "", breakageDegree: null }, ], firstHref: null, nextHref: "/repo%2Fmain/request/7?projects=opaque%2B%2F%3D", @@ -188,8 +188,11 @@ it("renders recorded project results, completion and shareable pagination", () = const table = within(projects).getByRole("table", { name: "Recorded project results" }); expect(within(table).queryByText(/Repository/)).toBeNull(); expect(within(table).getByRole("columnheader", { name: "Project" })).toBeVisible(); + expect(within(table).getByRole("columnheader", { name: "Project type" })).toBeVisible(); + expect(within(table).getByText("service")).toBeVisible(); + expect(within(table).getAllByText("Unknown")).toHaveLength(2); expect(within(projects).getByText("Green")).toBeVisible(); - expect(within(table).getByRole("row", { name: "partial-breakage-project Partial Breakage" })).toBeVisible(); + expect(within(table).getByRole("row", { name: "partial-breakage-project future-type Partial Breakage" })).toBeVisible(); expect(within(projects).getByText("Breakage")).toBeVisible(); expect(within(projects).getByText("No result recorded")).toBeVisible(); expect(within(projects).queryByText(/%/)).toBeNull(); @@ -212,7 +215,7 @@ it.each(["projects=next", "projectPageSize=25"])("preserves the request summary key: "/repo%2Fmain/request/7?" + query, projectStatus: { ok: true, data: { ...model.projectStatus.data, - projects: [{ project: "next-project", breakageDegree: 0 }], + projects: [{ project: "next-project", projectType: "", breakageDegree: 0 }], } }, }} />); expect(screen.getByText("git://repo/sha")).toBeVisible(); diff --git a/web/stovepipe/view/projects.tsx b/web/stovepipe/view/projects.tsx index ac6f0c87f..5a9c27362 100644 --- a/web/stovepipe/view/projects.tsx +++ b/web/stovepipe/view/projects.tsx @@ -59,10 +59,11 @@ export function ProjectResults({ model, pageKey }: { model: ProjectStatusModel; Results updated: {model.updatedAtMs === "0" ? "Unknown" : } {renderPagination("top")} - +
{model.projects.map((entry) => ( {entry.project} + {entry.projectType || "Unknown"} ))} diff --git a/web/test/e2e/stovepipe/run_e2e.py b/web/test/e2e/stovepipe/run_e2e.py index 83644d6de..2cc2540c4 100644 --- a/web/test/e2e/stovepipe/run_e2e.py +++ b/web/test/e2e/stovepipe/run_e2e.py @@ -46,7 +46,7 @@ def _pagination_fixture_sql() -> str: ) uri = "git://web/pagination/51?file=src%2Fmain#fragment" statements.append( - f"INSERT INTO validation_fact VALUES ('web/pagination','{uri}','',0.25,'51',2000);" + f"INSERT INTO validation_fact (queue,uri,project,degree,request_id,created_at) VALUES ('web/pagination','{uri}','',0.25,'51',2000);" ) for i in range(1, 52): degree = 1.0 if i == 50 else 0.25 if i == 1 else 0 @@ -54,7 +54,7 @@ def _pagination_fixture_sql() -> str: f"INSERT INTO request_log VALUES ('web/pagination','51','fact-{i}',{2000+i},'','validation_fact_recorded',1,'','{{}}');" ) statements.append( - f"INSERT INTO validation_fact VALUES ('web/pagination','{uri}','project-{i:02d}',{degree},'51',{2000+i});" + f"INSERT INTO validation_fact (queue,uri,project,degree,request_id,created_at) VALUES ('web/pagination','{uri}','project-{i:02d}',{degree},'51',{2000+i});" ) statements.append( f"INSERT INTO project_fact_by_degree VALUES ('web/pagination','{uri}',{degree},'project-{i:02d}');" diff --git a/web/test/package/react18/react18-runtime.test.mjs b/web/test/package/react18/react18-runtime.test.mjs index 4855fd0ed..01536c7f2 100644 --- a/web/test/package/react18/react18-runtime.test.mjs +++ b/web/test/package/react18/react18-runtime.test.mjs @@ -54,7 +54,7 @@ test("renders and hydrates packaged pages with React 18 and host themes", async queue: "repo/main", requestId: "1", updatedAtMs: 100n, repositoryResult: { case: "repositoryBreakageDegree", value: 0.25 }, projectResultsComplete: true, nextPageToken: "", - projects: [{ project: "example-project", result: { case: "breakageDegree", value: 0 } }], + projects: [{ project: "example-project", projectType: "service", result: { case: "breakageDegree", value: 0 } }], }), getRequestHistoryByID: async () => ({ requestSummary: { requestId: "1", queue: "repo/main", changeUri: "git://repo/main/sha", baseUri: "", requestState: "accepted", stateUpdatedAtMs: 100n, acceptedAtMs: 100n, outcomeReason: "", projectCount: 0, brokenProjectCount: 0 }, events: [] }), } }); @@ -116,7 +116,11 @@ test("renders and hydrates packaged pages with React 18 and host themes", async if (route === "/repo%2Fmain/request/1") { assert.match(container.textContent, /example-project/u); assert.match(container.textContent, /Repository resultPartial Breakage/u); - assert.match(container.textContent, /example-projectGreen/u); + const row = [...container.querySelectorAll('table[aria-label="Recorded project results"] tbody tr')] + .find(entry => entry.textContent.includes("example-project")); + assert.ok(row); + assert.deepEqual([...row.querySelectorAll("td")].map(cell => cell.textContent), + ["example-project", "service", "Green"]); assert.doesNotMatch(container.textContent, /%/u); } await act(async () => root.unmount());