diff --git a/app/controlplane/api/controlplane/v1/pagination.pb.go b/app/controlplane/api/controlplane/v1/pagination.pb.go index 294c90604..977df79b5 100644 --- a/app/controlplane/api/controlplane/v1/pagination.pb.go +++ b/app/controlplane/api/controlplane/v1/pagination.pb.go @@ -139,6 +139,140 @@ func (x *CursorPaginationRequest) GetLimit() int32 { return 0 } +// OffsetPaginationRequest is used to paginate the results +type OffsetPaginationRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The (zero-based) offset of the first item returned in the collection. + Page int32 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"` + // The maximum number of entries to return. If the value exceeds the maximum, then the maximum value will be used. + PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` +} + +func (x *OffsetPaginationRequest) Reset() { + *x = OffsetPaginationRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_pagination_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OffsetPaginationRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OffsetPaginationRequest) ProtoMessage() {} + +func (x *OffsetPaginationRequest) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_pagination_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OffsetPaginationRequest.ProtoReflect.Descriptor instead. +func (*OffsetPaginationRequest) Descriptor() ([]byte, []int) { + return file_controlplane_v1_pagination_proto_rawDescGZIP(), []int{2} +} + +func (x *OffsetPaginationRequest) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *OffsetPaginationRequest) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +// OffsetPaginationResponse is used to return the pagination information +type OffsetPaginationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The current page number + Page int32 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"` + // The number of results per page + PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"` + // The total number of results + TotalCount int32 `protobuf:"varint,3,opt,name=total_count,json=totalCount,proto3" json:"total_count,omitempty"` + // The total number of pages + TotalPages int32 `protobuf:"varint,4,opt,name=total_pages,json=totalPages,proto3" json:"total_pages,omitempty"` +} + +func (x *OffsetPaginationResponse) Reset() { + *x = OffsetPaginationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_v1_pagination_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OffsetPaginationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OffsetPaginationResponse) ProtoMessage() {} + +func (x *OffsetPaginationResponse) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_v1_pagination_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OffsetPaginationResponse.ProtoReflect.Descriptor instead. +func (*OffsetPaginationResponse) Descriptor() ([]byte, []int) { + return file_controlplane_v1_pagination_proto_rawDescGZIP(), []int{3} +} + +func (x *OffsetPaginationResponse) GetPage() int32 { + if x != nil { + return x.Page + } + return 0 +} + +func (x *OffsetPaginationResponse) GetPageSize() int32 { + if x != nil { + return x.PageSize + } + return 0 +} + +func (x *OffsetPaginationResponse) GetTotalCount() int32 { + if x != nil { + return x.TotalCount + } + return 0 +} + +func (x *OffsetPaginationResponse) GetTotalPages() int32 { + if x != nil { + return x.TotalPages + } + return 0 +} + var File_controlplane_v1_pagination_proto protoreflect.FileDescriptor var file_controlplane_v1_pagination_proto_rawDesc = []byte{ @@ -156,7 +290,22 @@ var file_controlplane_v1_pagination_proto_rawDesc = []byte{ 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x22, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x42, 0x0c, 0xba, 0x48, 0x09, 0xd8, 0x01, 0x01, 0x1a, 0x04, 0x18, 0x64, 0x28, 0x02, 0x52, 0x05, 0x6c, - 0x69, 0x6d, 0x69, 0x74, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x69, 0x6d, 0x69, 0x74, 0x22, 0x5e, 0x0a, 0x17, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x50, 0x61, + 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1b, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x42, 0x07, 0xba, + 0x48, 0x04, 0x1a, 0x02, 0x28, 0x01, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x09, + 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x42, + 0x09, 0xba, 0x48, 0x06, 0x1a, 0x04, 0x18, 0x64, 0x20, 0x00, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, + 0x53, 0x69, 0x7a, 0x65, 0x22, 0x8d, 0x01, 0x0a, 0x18, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x50, + 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x69, + 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53, 0x69, + 0x7a, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x67, + 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, + 0x61, 0x67, 0x65, 0x73, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, @@ -176,10 +325,12 @@ func file_controlplane_v1_pagination_proto_rawDescGZIP() []byte { return file_controlplane_v1_pagination_proto_rawDescData } -var file_controlplane_v1_pagination_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_controlplane_v1_pagination_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_controlplane_v1_pagination_proto_goTypes = []interface{}{ (*CursorPaginationResponse)(nil), // 0: controlplane.v1.CursorPaginationResponse (*CursorPaginationRequest)(nil), // 1: controlplane.v1.CursorPaginationRequest + (*OffsetPaginationRequest)(nil), // 2: controlplane.v1.OffsetPaginationRequest + (*OffsetPaginationResponse)(nil), // 3: controlplane.v1.OffsetPaginationResponse } var file_controlplane_v1_pagination_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type @@ -219,6 +370,30 @@ func file_controlplane_v1_pagination_proto_init() { return nil } } + file_controlplane_v1_pagination_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OffsetPaginationRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_v1_pagination_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OffsetPaginationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -226,7 +401,7 @@ func file_controlplane_v1_pagination_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controlplane_v1_pagination_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/app/controlplane/api/controlplane/v1/pagination.proto b/app/controlplane/api/controlplane/v1/pagination.proto index 5652fc5c3..2bb328948 100644 --- a/app/controlplane/api/controlplane/v1/pagination.proto +++ b/app/controlplane/api/controlplane/v1/pagination.proto @@ -36,3 +36,23 @@ message CursorPaginationRequest { (buf.validate.field).ignore = IGNORE_IF_UNPOPULATED ]; } + +// OffsetPaginationRequest is used to paginate the results +message OffsetPaginationRequest { + // The (zero-based) offset of the first item returned in the collection. + int32 page = 1 [(buf.validate.field).int32.gte = 1]; + // The maximum number of entries to return. If the value exceeds the maximum, then the maximum value will be used. + int32 page_size = 2 [(buf.validate.field).int32.gt = 0, (buf.validate.field).int32.lte = 100]; +} + +// OffsetPaginationResponse is used to return the pagination information +message OffsetPaginationResponse { + // The current page number + int32 page = 1; + // The number of results per page + int32 page_size = 2; + // The total number of results + int32 total_count = 3; + // The total number of pages + int32 total_pages = 4; +} diff --git a/app/controlplane/api/controlplane/v1/workflow.pb.go b/app/controlplane/api/controlplane/v1/workflow.pb.go index 5a5dd27bd..6cd59c84d 100644 --- a/app/controlplane/api/controlplane/v1/workflow.pb.go +++ b/app/controlplane/api/controlplane/v1/workflow.pb.go @@ -23,6 +23,7 @@ package v1 import ( _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + v1 "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -36,6 +37,59 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// WorkflowActivityWindow represents the time window for the last known workflow activity. +type WorkflowActivityWindow int32 + +const ( + WorkflowActivityWindow_WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED WorkflowActivityWindow = 0 + WorkflowActivityWindow_WORKFLOW_ACTIVITY_WINDOW_LAST_DAY WorkflowActivityWindow = 1 + WorkflowActivityWindow_WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS WorkflowActivityWindow = 2 + WorkflowActivityWindow_WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS WorkflowActivityWindow = 3 +) + +// Enum value maps for WorkflowActivityWindow. +var ( + WorkflowActivityWindow_name = map[int32]string{ + 0: "WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED", + 1: "WORKFLOW_ACTIVITY_WINDOW_LAST_DAY", + 2: "WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS", + 3: "WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS", + } + WorkflowActivityWindow_value = map[string]int32{ + "WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED": 0, + "WORKFLOW_ACTIVITY_WINDOW_LAST_DAY": 1, + "WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS": 2, + "WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS": 3, + } +) + +func (x WorkflowActivityWindow) Enum() *WorkflowActivityWindow { + p := new(WorkflowActivityWindow) + *p = x + return p +} + +func (x WorkflowActivityWindow) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WorkflowActivityWindow) Descriptor() protoreflect.EnumDescriptor { + return file_controlplane_v1_workflow_proto_enumTypes[0].Descriptor() +} + +func (WorkflowActivityWindow) Type() protoreflect.EnumType { + return &file_controlplane_v1_workflow_proto_enumTypes[0] +} + +func (x WorkflowActivityWindow) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WorkflowActivityWindow.Descriptor instead. +func (WorkflowActivityWindow) EnumDescriptor() ([]byte, []int) { + return file_controlplane_v1_workflow_proto_rawDescGZIP(), []int{0} +} + type WorkflowServiceCreateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -405,7 +459,22 @@ type WorkflowServiceListRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ProjectReference *EntityRef `protobuf:"bytes,1,opt,name=project_reference,json=projectReference,proto3,oneof" json:"project_reference,omitempty"` + // The name of the workflow to filter by + WorkflowName string `protobuf:"bytes,1,opt,name=workflow_name,json=workflowName,proto3" json:"workflow_name,omitempty"` + // The team the workflow belongs to + WorkflowTeam string `protobuf:"bytes,2,opt,name=workflow_team,json=workflowTeam,proto3" json:"workflow_team,omitempty"` + // The project the workflow belongs to + ProjectNames []string `protobuf:"bytes,3,rep,name=project_names,json=projectNames,proto3" json:"project_names,omitempty"` + // If the workflow is public + WorkflowPublic *bool `protobuf:"varint,4,opt,name=workflow_public,json=workflowPublic,proto3,oneof" json:"workflow_public,omitempty"` + // The type of runner that ran the workflow + WorkflowRunRunnerType v1.CraftingSchema_Runner_RunnerType `protobuf:"varint,5,opt,name=workflow_run_runner_type,json=workflowRunRunnerType,proto3,enum=workflowcontract.v1.CraftingSchema_Runner_RunnerType" json:"workflow_run_runner_type,omitempty"` + // The status of the last workflow run + WorkflowRunLastStatus RunStatus `protobuf:"varint,6,opt,name=workflow_run_last_status,json=workflowRunLastStatus,proto3,enum=controlplane.v1.RunStatus" json:"workflow_run_last_status,omitempty"` + // The time window for the last known workflow activity + WorkflowLastActivityWindow WorkflowActivityWindow `protobuf:"varint,7,opt,name=workflow_last_activity_window,json=workflowLastActivityWindow,proto3,enum=controlplane.v1.WorkflowActivityWindow" json:"workflow_last_activity_window,omitempty"` + // Pagination options + Pagination *OffsetPaginationRequest `protobuf:"bytes,8,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (x *WorkflowServiceListRequest) Reset() { @@ -440,9 +509,58 @@ func (*WorkflowServiceListRequest) Descriptor() ([]byte, []int) { return file_controlplane_v1_workflow_proto_rawDescGZIP(), []int{6} } -func (x *WorkflowServiceListRequest) GetProjectReference() *EntityRef { +func (x *WorkflowServiceListRequest) GetWorkflowName() string { if x != nil { - return x.ProjectReference + return x.WorkflowName + } + return "" +} + +func (x *WorkflowServiceListRequest) GetWorkflowTeam() string { + if x != nil { + return x.WorkflowTeam + } + return "" +} + +func (x *WorkflowServiceListRequest) GetProjectNames() []string { + if x != nil { + return x.ProjectNames + } + return nil +} + +func (x *WorkflowServiceListRequest) GetWorkflowPublic() bool { + if x != nil && x.WorkflowPublic != nil { + return *x.WorkflowPublic + } + return false +} + +func (x *WorkflowServiceListRequest) GetWorkflowRunRunnerType() v1.CraftingSchema_Runner_RunnerType { + if x != nil { + return x.WorkflowRunRunnerType + } + return v1.CraftingSchema_Runner_RunnerType(0) +} + +func (x *WorkflowServiceListRequest) GetWorkflowRunLastStatus() RunStatus { + if x != nil { + return x.WorkflowRunLastStatus + } + return RunStatus_RUN_STATUS_UNSPECIFIED +} + +func (x *WorkflowServiceListRequest) GetWorkflowLastActivityWindow() WorkflowActivityWindow { + if x != nil { + return x.WorkflowLastActivityWindow + } + return WorkflowActivityWindow_WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED +} + +func (x *WorkflowServiceListRequest) GetPagination() *OffsetPaginationRequest { + if x != nil { + return x.Pagination } return nil } @@ -452,7 +570,8 @@ type WorkflowServiceListResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Result []*WorkflowItem `protobuf:"bytes,1,rep,name=result,proto3" json:"result,omitempty"` + Result []*WorkflowItem `protobuf:"bytes,1,rep,name=result,proto3" json:"result,omitempty"` + Pagination *OffsetPaginationResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` } func (x *WorkflowServiceListResponse) Reset() { @@ -494,6 +613,13 @@ func (x *WorkflowServiceListResponse) GetResult() []*WorkflowItem { return nil } +func (x *WorkflowServiceListResponse) GetPagination() *OffsetPaginationResponse { + if x != nil { + return x.Pagination + } + return nil +} + type WorkflowServiceViewRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -606,114 +732,13 @@ var file_controlplane_v1_workflow_proto_rawDesc = []byte{ 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe1, 0x03, 0x0a, 0x1c, 0x57, 0x6f, 0x72, 0x6b, - 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, - 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, - 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, - 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, - 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, - 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, - 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, - 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0xac, - 0x01, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x86, 0x01, 0xba, 0x48, 0x82, 0x01, 0xba, 0x01, 0x7c, - 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, - 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, - 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, - 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, - 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, - 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, - 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0xd0, 0x01, 0x01, 0x52, - 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x61, - 0x6d, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x22, 0xa7, 0x04, 0x0a, 0x1c, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x97, 0x01, 0x0a, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, - 0x7f, 0xba, 0x01, 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, - 0x31, 0x32, 0x33, 0x12, 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, - 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, - 0x2f, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, - 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, - 0x39, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x48, 0x00, 0x52, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x06, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, - 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, - 0xad, 0x01, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, - 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, - 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, - 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, - 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, - 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, - 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, - 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x48, 0x03, 0x52, 0x0c, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, - 0x07, 0x0a, 0x05, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x56, 0x0a, 0x1d, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x56, 0x0a, - 0x1d, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, - 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x1c, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, 0x0a, 0x0d, - 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, 0x3a, 0x6d, - 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, 0x6c, 0x79, - 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, - 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, - 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, - 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, - 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x2a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x1f, 0x0a, 0x1d, - 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x80, 0x01, - 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x11, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x52, 0x65, 0x66, 0x48, 0x00, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, - 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x88, 0x01, 0x01, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x22, 0x54, 0x0a, 0x1b, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x35, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xe2, 0x01, 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x52, 0x65, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x29, 0x77, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x63, + 0x72, 0x61, 0x66, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe1, 0x03, 0x0a, 0x1c, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, 0x3a, 0x6d, 0x75, @@ -726,51 +751,205 @@ var file_controlplane_v1_workflow_proto_rawDesc = []byte{ 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x54, 0x0a, 0x1b, 0x57, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x56, 0x69, - 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0xac, 0x01, 0x0a, 0x0d, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x86, 0x01, 0xba, 0x48, 0x82, 0x01, 0xba, 0x01, 0x7c, 0x0a, 0x0d, 0x6e, + 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, 0x3a, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, + 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, + 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, + 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, + 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0xd0, 0x01, 0x01, 0x52, 0x0c, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, + 0x61, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x12, 0x20, + 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x22, 0xa7, 0x04, 0x0a, 0x1c, 0x57, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, + 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, + 0x12, 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, + 0x6e, 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, + 0x74, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, + 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, + 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, + 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x17, 0x0a, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x04, 0x74, 0x65, 0x61, 0x6d, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0xad, 0x01, 0x0a, + 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, 0x0a, 0x0d, 0x6e, + 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, 0x3a, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, + 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, + 0x73, 0x2c, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, + 0x5d, 0x28, 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, + 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x48, 0x03, 0x52, 0x0c, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x61, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x07, 0x0a, 0x05, + 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x56, 0x0a, 0x1d, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x74, + 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x56, 0x0a, 0x1d, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x22, 0xe4, 0x01, 0x0a, 0x1c, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, + 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, 0x3a, 0x6d, 0x75, 0x73, 0x74, + 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6c, 0x6f, + 0x77, 0x65, 0x72, 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x73, 0x2c, + 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x79, + 0x70, 0x68, 0x65, 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, + 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, + 0x5b, 0x2d, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, + 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, + 0x0c, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x1f, 0x0a, 0x1d, 0x57, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xf2, 0x04, 0x0a, 0x1a, 0x57, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x77, 0x6f, 0x72, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, + 0x0a, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x74, 0x65, 0x61, 0x6d, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, + 0x65, 0x61, 0x6d, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x08, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x88, 0x01, 0x01, 0x12, 0x7c, 0x0a, 0x18, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x35, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x61, 0x66, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x52, 0x75, + 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x42, + 0x0c, 0xba, 0x48, 0x09, 0xd0, 0x01, 0x01, 0x82, 0x01, 0x03, 0x22, 0x01, 0x00, 0x52, 0x15, 0x77, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x75, 0x6e, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x61, 0x0a, 0x18, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x42, 0x0c, 0xba, 0x48, 0x09, 0xd0, 0x01, 0x01, 0x82, 0x01, 0x03, 0x22, 0x01, 0x00, + 0x52, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x75, 0x6e, 0x4c, 0x61, 0x73, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x78, 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, + 0x79, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, + 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, + 0x79, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x42, 0x0c, 0xba, 0x48, 0x09, 0xd0, 0x01, 0x01, 0x82, + 0x01, 0x03, 0x22, 0x01, 0x00, 0x52, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4c, + 0x61, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x57, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x12, 0x48, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x50, 0x61, + 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, + 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x12, 0x0a, 0x10, 0x5f, + 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x22, + 0x9f, 0x01, 0x0a, 0x1b, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x35, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x49, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x50, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x22, 0xe2, 0x01, 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, + 0x6e, 0x73, 0x2d, 0x31, 0x31, 0x32, 0x33, 0x12, 0x3a, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, + 0x63, 0x61, 0x73, 0x65, 0x20, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, + 0x6e, 0x73, 0x2e, 0x1a, 0x2f, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, + 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x5b, 0x2d, 0x61, + 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x30, 0x2d, 0x39, 0x5d, 0x29, + 0x3f, 0x24, 0x27, 0x29, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2a, 0x0a, 0x0c, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x54, 0x0a, 0x1b, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, + 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, + 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, + 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2a, 0xbe, 0x01, 0x0a, + 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, + 0x79, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x28, 0x0a, 0x24, 0x57, 0x4f, 0x52, 0x4b, 0x46, + 0x4c, 0x4f, 0x57, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f, 0x57, 0x49, 0x4e, + 0x44, 0x4f, 0x57, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x25, 0x0a, 0x21, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x5f, 0x4c, 0x41, + 0x53, 0x54, 0x5f, 0x44, 0x41, 0x59, 0x10, 0x01, 0x12, 0x28, 0x0a, 0x24, 0x57, 0x4f, 0x52, 0x4b, + 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f, 0x57, 0x49, + 0x4e, 0x44, 0x4f, 0x57, 0x5f, 0x4c, 0x41, 0x53, 0x54, 0x5f, 0x37, 0x5f, 0x44, 0x41, 0x59, 0x53, + 0x10, 0x02, 0x12, 0x29, 0x0a, 0x25, 0x57, 0x4f, 0x52, 0x4b, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x41, + 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x57, 0x5f, 0x4c, + 0x41, 0x53, 0x54, 0x5f, 0x33, 0x30, 0x5f, 0x44, 0x41, 0x59, 0x53, 0x10, 0x03, 0x32, 0x92, 0x04, + 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x67, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, - 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x32, 0x92, 0x04, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x67, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, - 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, + 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x06, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2b, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, + 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x04, 0x56, 0x69, 0x65, 0x77, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, - 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, - 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, - 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x04, 0x56, 0x69, - 0x65, 0x77, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x56, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x56, 0x69, 0x65, + 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, 0x06, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x56, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x56, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x67, 0x0a, - 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, - 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, - 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, - 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, - 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x4c, 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -785,42 +964,51 @@ func file_controlplane_v1_workflow_proto_rawDescGZIP() []byte { return file_controlplane_v1_workflow_proto_rawDescData } +var file_controlplane_v1_workflow_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_controlplane_v1_workflow_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_controlplane_v1_workflow_proto_goTypes = []interface{}{ - (*WorkflowServiceCreateRequest)(nil), // 0: controlplane.v1.WorkflowServiceCreateRequest - (*WorkflowServiceUpdateRequest)(nil), // 1: controlplane.v1.WorkflowServiceUpdateRequest - (*WorkflowServiceUpdateResponse)(nil), // 2: controlplane.v1.WorkflowServiceUpdateResponse - (*WorkflowServiceCreateResponse)(nil), // 3: controlplane.v1.WorkflowServiceCreateResponse - (*WorkflowServiceDeleteRequest)(nil), // 4: controlplane.v1.WorkflowServiceDeleteRequest - (*WorkflowServiceDeleteResponse)(nil), // 5: controlplane.v1.WorkflowServiceDeleteResponse - (*WorkflowServiceListRequest)(nil), // 6: controlplane.v1.WorkflowServiceListRequest - (*WorkflowServiceListResponse)(nil), // 7: controlplane.v1.WorkflowServiceListResponse - (*WorkflowServiceViewRequest)(nil), // 8: controlplane.v1.WorkflowServiceViewRequest - (*WorkflowServiceViewResponse)(nil), // 9: controlplane.v1.WorkflowServiceViewResponse - (*WorkflowItem)(nil), // 10: controlplane.v1.WorkflowItem - (*EntityRef)(nil), // 11: controlplane.v1.EntityRef + (WorkflowActivityWindow)(0), // 0: controlplane.v1.WorkflowActivityWindow + (*WorkflowServiceCreateRequest)(nil), // 1: controlplane.v1.WorkflowServiceCreateRequest + (*WorkflowServiceUpdateRequest)(nil), // 2: controlplane.v1.WorkflowServiceUpdateRequest + (*WorkflowServiceUpdateResponse)(nil), // 3: controlplane.v1.WorkflowServiceUpdateResponse + (*WorkflowServiceCreateResponse)(nil), // 4: controlplane.v1.WorkflowServiceCreateResponse + (*WorkflowServiceDeleteRequest)(nil), // 5: controlplane.v1.WorkflowServiceDeleteRequest + (*WorkflowServiceDeleteResponse)(nil), // 6: controlplane.v1.WorkflowServiceDeleteResponse + (*WorkflowServiceListRequest)(nil), // 7: controlplane.v1.WorkflowServiceListRequest + (*WorkflowServiceListResponse)(nil), // 8: controlplane.v1.WorkflowServiceListResponse + (*WorkflowServiceViewRequest)(nil), // 9: controlplane.v1.WorkflowServiceViewRequest + (*WorkflowServiceViewResponse)(nil), // 10: controlplane.v1.WorkflowServiceViewResponse + (*WorkflowItem)(nil), // 11: controlplane.v1.WorkflowItem + (v1.CraftingSchema_Runner_RunnerType)(0), // 12: workflowcontract.v1.CraftingSchema.Runner.RunnerType + (RunStatus)(0), // 13: controlplane.v1.RunStatus + (*OffsetPaginationRequest)(nil), // 14: controlplane.v1.OffsetPaginationRequest + (*OffsetPaginationResponse)(nil), // 15: controlplane.v1.OffsetPaginationResponse } var file_controlplane_v1_workflow_proto_depIdxs = []int32{ - 10, // 0: controlplane.v1.WorkflowServiceUpdateResponse.result:type_name -> controlplane.v1.WorkflowItem - 10, // 1: controlplane.v1.WorkflowServiceCreateResponse.result:type_name -> controlplane.v1.WorkflowItem - 11, // 2: controlplane.v1.WorkflowServiceListRequest.project_reference:type_name -> controlplane.v1.EntityRef - 10, // 3: controlplane.v1.WorkflowServiceListResponse.result:type_name -> controlplane.v1.WorkflowItem - 10, // 4: controlplane.v1.WorkflowServiceViewResponse.result:type_name -> controlplane.v1.WorkflowItem - 0, // 5: controlplane.v1.WorkflowService.Create:input_type -> controlplane.v1.WorkflowServiceCreateRequest - 1, // 6: controlplane.v1.WorkflowService.Update:input_type -> controlplane.v1.WorkflowServiceUpdateRequest - 6, // 7: controlplane.v1.WorkflowService.List:input_type -> controlplane.v1.WorkflowServiceListRequest - 8, // 8: controlplane.v1.WorkflowService.View:input_type -> controlplane.v1.WorkflowServiceViewRequest - 4, // 9: controlplane.v1.WorkflowService.Delete:input_type -> controlplane.v1.WorkflowServiceDeleteRequest - 3, // 10: controlplane.v1.WorkflowService.Create:output_type -> controlplane.v1.WorkflowServiceCreateResponse - 2, // 11: controlplane.v1.WorkflowService.Update:output_type -> controlplane.v1.WorkflowServiceUpdateResponse - 7, // 12: controlplane.v1.WorkflowService.List:output_type -> controlplane.v1.WorkflowServiceListResponse - 9, // 13: controlplane.v1.WorkflowService.View:output_type -> controlplane.v1.WorkflowServiceViewResponse - 5, // 14: controlplane.v1.WorkflowService.Delete:output_type -> controlplane.v1.WorkflowServiceDeleteResponse - 10, // [10:15] is the sub-list for method output_type - 5, // [5:10] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 11, // 0: controlplane.v1.WorkflowServiceUpdateResponse.result:type_name -> controlplane.v1.WorkflowItem + 11, // 1: controlplane.v1.WorkflowServiceCreateResponse.result:type_name -> controlplane.v1.WorkflowItem + 12, // 2: controlplane.v1.WorkflowServiceListRequest.workflow_run_runner_type:type_name -> workflowcontract.v1.CraftingSchema.Runner.RunnerType + 13, // 3: controlplane.v1.WorkflowServiceListRequest.workflow_run_last_status:type_name -> controlplane.v1.RunStatus + 0, // 4: controlplane.v1.WorkflowServiceListRequest.workflow_last_activity_window:type_name -> controlplane.v1.WorkflowActivityWindow + 14, // 5: controlplane.v1.WorkflowServiceListRequest.pagination:type_name -> controlplane.v1.OffsetPaginationRequest + 11, // 6: controlplane.v1.WorkflowServiceListResponse.result:type_name -> controlplane.v1.WorkflowItem + 15, // 7: controlplane.v1.WorkflowServiceListResponse.pagination:type_name -> controlplane.v1.OffsetPaginationResponse + 11, // 8: controlplane.v1.WorkflowServiceViewResponse.result:type_name -> controlplane.v1.WorkflowItem + 1, // 9: controlplane.v1.WorkflowService.Create:input_type -> controlplane.v1.WorkflowServiceCreateRequest + 2, // 10: controlplane.v1.WorkflowService.Update:input_type -> controlplane.v1.WorkflowServiceUpdateRequest + 7, // 11: controlplane.v1.WorkflowService.List:input_type -> controlplane.v1.WorkflowServiceListRequest + 9, // 12: controlplane.v1.WorkflowService.View:input_type -> controlplane.v1.WorkflowServiceViewRequest + 5, // 13: controlplane.v1.WorkflowService.Delete:input_type -> controlplane.v1.WorkflowServiceDeleteRequest + 4, // 14: controlplane.v1.WorkflowService.Create:output_type -> controlplane.v1.WorkflowServiceCreateResponse + 3, // 15: controlplane.v1.WorkflowService.Update:output_type -> controlplane.v1.WorkflowServiceUpdateResponse + 8, // 16: controlplane.v1.WorkflowService.List:output_type -> controlplane.v1.WorkflowServiceListResponse + 10, // 17: controlplane.v1.WorkflowService.View:output_type -> controlplane.v1.WorkflowServiceViewResponse + 6, // 18: controlplane.v1.WorkflowService.Delete:output_type -> controlplane.v1.WorkflowServiceDeleteResponse + 14, // [14:19] is the sub-list for method output_type + 9, // [9:14] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name } func init() { file_controlplane_v1_workflow_proto_init() } @@ -829,6 +1017,7 @@ func file_controlplane_v1_workflow_proto_init() { return } file_controlplane_v1_response_messages_proto_init() + file_controlplane_v1_pagination_proto_init() if !protoimpl.UnsafeEnabled { file_controlplane_v1_workflow_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*WorkflowServiceCreateRequest); i { @@ -958,13 +1147,14 @@ func file_controlplane_v1_workflow_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controlplane_v1_workflow_proto_rawDesc, - NumEnums: 0, + NumEnums: 1, NumMessages: 10, NumExtensions: 0, NumServices: 1, }, GoTypes: file_controlplane_v1_workflow_proto_goTypes, DependencyIndexes: file_controlplane_v1_workflow_proto_depIdxs, + EnumInfos: file_controlplane_v1_workflow_proto_enumTypes, MessageInfos: file_controlplane_v1_workflow_proto_msgTypes, }.Build() File_controlplane_v1_workflow_proto = out.File diff --git a/app/controlplane/api/controlplane/v1/workflow.proto b/app/controlplane/api/controlplane/v1/workflow.proto index a1e998631..e09290f83 100644 --- a/app/controlplane/api/controlplane/v1/workflow.proto +++ b/app/controlplane/api/controlplane/v1/workflow.proto @@ -19,6 +19,8 @@ package controlplane.v1; import "buf/validate/validate.proto"; import "controlplane/v1/response_messages.proto"; +import "controlplane/v1/pagination.proto"; +import "workflowcontract/v1/crafting_schema.proto"; option go_package = "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1;v1"; @@ -102,10 +104,32 @@ message WorkflowServiceDeleteRequest { message WorkflowServiceDeleteResponse {} message WorkflowServiceListRequest { - optional EntityRef project_reference = 1; + // The name of the workflow to filter by + string workflow_name = 1; + // The team the workflow belongs to + string workflow_team = 2; + // The project the workflow belongs to + repeated string project_names = 3; + // If the workflow is public + optional bool workflow_public = 4; + // The type of runner that ran the workflow + workflowcontract.v1.CraftingSchema.Runner.RunnerType workflow_run_runner_type = 5 [(buf.validate.field).enum = { + not_in: [0] + }, (buf.validate.field).ignore_empty = true]; + // The status of the last workflow run + RunStatus workflow_run_last_status = 6 [(buf.validate.field).enum = { + not_in: [0] + }, (buf.validate.field).ignore_empty = true]; + // The time window for the last known workflow activity + WorkflowActivityWindow workflow_last_activity_window = 7 [(buf.validate.field).enum = { + not_in: [0] + }, (buf.validate.field).ignore_empty = true]; + // Pagination options + OffsetPaginationRequest pagination = 8; } message WorkflowServiceListResponse { repeated WorkflowItem result = 1; + OffsetPaginationResponse pagination = 2; } message WorkflowServiceViewRequest { @@ -123,3 +147,11 @@ message WorkflowServiceViewRequest { message WorkflowServiceViewResponse { WorkflowItem result = 1; } + +// WorkflowActivityWindow represents the time window for the last known workflow activity. +enum WorkflowActivityWindow { + WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED = 0; + WORKFLOW_ACTIVITY_WINDOW_LAST_DAY = 1; + WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS = 2; + WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS = 3; +} \ No newline at end of file diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/pagination.ts b/app/controlplane/api/gen/frontend/controlplane/v1/pagination.ts index 20cf0db74..3e82cc153 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/pagination.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/pagination.ts @@ -13,6 +13,26 @@ export interface CursorPaginationRequest { limit: number; } +/** OffsetPaginationRequest is used to paginate the results */ +export interface OffsetPaginationRequest { + /** The (zero-based) offset of the first item returned in the collection. */ + page: number; + /** The maximum number of entries to return. If the value exceeds the maximum, then the maximum value will be used. */ + pageSize: number; +} + +/** OffsetPaginationResponse is used to return the pagination information */ +export interface OffsetPaginationResponse { + /** The current page number */ + page: number; + /** The number of results per page */ + pageSize: number; + /** The total number of results */ + totalCount: number; + /** The total number of pages */ + totalPages: number; +} + function createBaseCursorPaginationResponse(): CursorPaginationResponse { return { nextCursor: "" }; } @@ -140,6 +160,174 @@ export const CursorPaginationRequest = { }, }; +function createBaseOffsetPaginationRequest(): OffsetPaginationRequest { + return { page: 0, pageSize: 0 }; +} + +export const OffsetPaginationRequest = { + encode(message: OffsetPaginationRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.page !== 0) { + writer.uint32(8).int32(message.page); + } + if (message.pageSize !== 0) { + writer.uint32(16).int32(message.pageSize); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): OffsetPaginationRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseOffsetPaginationRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.page = reader.int32(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.pageSize = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): OffsetPaginationRequest { + return { + page: isSet(object.page) ? Number(object.page) : 0, + pageSize: isSet(object.pageSize) ? Number(object.pageSize) : 0, + }; + }, + + toJSON(message: OffsetPaginationRequest): unknown { + const obj: any = {}; + message.page !== undefined && (obj.page = Math.round(message.page)); + message.pageSize !== undefined && (obj.pageSize = Math.round(message.pageSize)); + return obj; + }, + + create, I>>(base?: I): OffsetPaginationRequest { + return OffsetPaginationRequest.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): OffsetPaginationRequest { + const message = createBaseOffsetPaginationRequest(); + message.page = object.page ?? 0; + message.pageSize = object.pageSize ?? 0; + return message; + }, +}; + +function createBaseOffsetPaginationResponse(): OffsetPaginationResponse { + return { page: 0, pageSize: 0, totalCount: 0, totalPages: 0 }; +} + +export const OffsetPaginationResponse = { + encode(message: OffsetPaginationResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.page !== 0) { + writer.uint32(8).int32(message.page); + } + if (message.pageSize !== 0) { + writer.uint32(16).int32(message.pageSize); + } + if (message.totalCount !== 0) { + writer.uint32(24).int32(message.totalCount); + } + if (message.totalPages !== 0) { + writer.uint32(32).int32(message.totalPages); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): OffsetPaginationResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseOffsetPaginationResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.page = reader.int32(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.pageSize = reader.int32(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.totalCount = reader.int32(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.totalPages = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): OffsetPaginationResponse { + return { + page: isSet(object.page) ? Number(object.page) : 0, + pageSize: isSet(object.pageSize) ? Number(object.pageSize) : 0, + totalCount: isSet(object.totalCount) ? Number(object.totalCount) : 0, + totalPages: isSet(object.totalPages) ? Number(object.totalPages) : 0, + }; + }, + + toJSON(message: OffsetPaginationResponse): unknown { + const obj: any = {}; + message.page !== undefined && (obj.page = Math.round(message.page)); + message.pageSize !== undefined && (obj.pageSize = Math.round(message.pageSize)); + message.totalCount !== undefined && (obj.totalCount = Math.round(message.totalCount)); + message.totalPages !== undefined && (obj.totalPages = Math.round(message.totalPages)); + return obj; + }, + + create, I>>(base?: I): OffsetPaginationResponse { + return OffsetPaginationResponse.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): OffsetPaginationResponse { + const message = createBaseOffsetPaginationResponse(); + message.page = object.page ?? 0; + message.pageSize = object.pageSize ?? 0; + message.totalCount = object.totalCount ?? 0; + message.totalPages = object.totalPages ?? 0; + return message; + }, +}; + type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; export type DeepPartial = T extends Builtin ? T diff --git a/app/controlplane/api/gen/frontend/controlplane/v1/workflow.ts b/app/controlplane/api/gen/frontend/controlplane/v1/workflow.ts index a29381cb0..bf13cf135 100644 --- a/app/controlplane/api/gen/frontend/controlplane/v1/workflow.ts +++ b/app/controlplane/api/gen/frontend/controlplane/v1/workflow.ts @@ -2,10 +2,62 @@ import { grpc } from "@improbable-eng/grpc-web"; import { BrowserHeaders } from "browser-headers"; import _m0 from "protobufjs/minimal"; -import { EntityRef, WorkflowItem } from "./response_messages"; +import { + CraftingSchema_Runner_RunnerType, + craftingSchema_Runner_RunnerTypeFromJSON, + craftingSchema_Runner_RunnerTypeToJSON, +} from "../../workflowcontract/v1/crafting_schema"; +import { OffsetPaginationRequest, OffsetPaginationResponse } from "./pagination"; +import { RunStatus, runStatusFromJSON, runStatusToJSON, WorkflowItem } from "./response_messages"; export const protobufPackage = "controlplane.v1"; +/** WorkflowActivityWindow represents the time window for the last known workflow activity. */ +export enum WorkflowActivityWindow { + WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED = 0, + WORKFLOW_ACTIVITY_WINDOW_LAST_DAY = 1, + WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS = 2, + WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS = 3, + UNRECOGNIZED = -1, +} + +export function workflowActivityWindowFromJSON(object: any): WorkflowActivityWindow { + switch (object) { + case 0: + case "WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED": + return WorkflowActivityWindow.WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED; + case 1: + case "WORKFLOW_ACTIVITY_WINDOW_LAST_DAY": + return WorkflowActivityWindow.WORKFLOW_ACTIVITY_WINDOW_LAST_DAY; + case 2: + case "WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS": + return WorkflowActivityWindow.WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS; + case 3: + case "WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS": + return WorkflowActivityWindow.WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS; + case -1: + case "UNRECOGNIZED": + default: + return WorkflowActivityWindow.UNRECOGNIZED; + } +} + +export function workflowActivityWindowToJSON(object: WorkflowActivityWindow): string { + switch (object) { + case WorkflowActivityWindow.WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED: + return "WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED"; + case WorkflowActivityWindow.WORKFLOW_ACTIVITY_WINDOW_LAST_DAY: + return "WORKFLOW_ACTIVITY_WINDOW_LAST_DAY"; + case WorkflowActivityWindow.WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS: + return "WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS"; + case WorkflowActivityWindow.WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS: + return "WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS"; + case WorkflowActivityWindow.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + export interface WorkflowServiceCreateRequest { name: string; projectName: string; @@ -46,11 +98,29 @@ export interface WorkflowServiceDeleteResponse { } export interface WorkflowServiceListRequest { - projectReference?: EntityRef | undefined; + /** The name of the workflow to filter by */ + workflowName: string; + /** The team the workflow belongs to */ + workflowTeam: string; + /** The project the workflow belongs to */ + projectNames: string[]; + /** If the workflow is public */ + workflowPublic?: + | boolean + | undefined; + /** The type of runner that ran the workflow */ + workflowRunRunnerType: CraftingSchema_Runner_RunnerType; + /** The status of the last workflow run */ + workflowRunLastStatus: RunStatus; + /** The time window for the last known workflow activity */ + workflowLastActivityWindow: WorkflowActivityWindow; + /** Pagination options */ + pagination?: OffsetPaginationRequest; } export interface WorkflowServiceListResponse { result: WorkflowItem[]; + pagination?: OffsetPaginationResponse; } export interface WorkflowServiceViewRequest { @@ -551,13 +621,43 @@ export const WorkflowServiceDeleteResponse = { }; function createBaseWorkflowServiceListRequest(): WorkflowServiceListRequest { - return { projectReference: undefined }; + return { + workflowName: "", + workflowTeam: "", + projectNames: [], + workflowPublic: undefined, + workflowRunRunnerType: 0, + workflowRunLastStatus: 0, + workflowLastActivityWindow: 0, + pagination: undefined, + }; } export const WorkflowServiceListRequest = { encode(message: WorkflowServiceListRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.projectReference !== undefined) { - EntityRef.encode(message.projectReference, writer.uint32(10).fork()).ldelim(); + if (message.workflowName !== "") { + writer.uint32(10).string(message.workflowName); + } + if (message.workflowTeam !== "") { + writer.uint32(18).string(message.workflowTeam); + } + for (const v of message.projectNames) { + writer.uint32(26).string(v!); + } + if (message.workflowPublic !== undefined) { + writer.uint32(32).bool(message.workflowPublic); + } + if (message.workflowRunRunnerType !== 0) { + writer.uint32(40).int32(message.workflowRunRunnerType); + } + if (message.workflowRunLastStatus !== 0) { + writer.uint32(48).int32(message.workflowRunLastStatus); + } + if (message.workflowLastActivityWindow !== 0) { + writer.uint32(56).int32(message.workflowLastActivityWindow); + } + if (message.pagination !== undefined) { + OffsetPaginationRequest.encode(message.pagination, writer.uint32(66).fork()).ldelim(); } return writer; }, @@ -574,7 +674,56 @@ export const WorkflowServiceListRequest = { break; } - message.projectReference = EntityRef.decode(reader, reader.uint32()); + message.workflowName = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.workflowTeam = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.projectNames.push(reader.string()); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.workflowPublic = reader.bool(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.workflowRunRunnerType = reader.int32() as any; + continue; + case 6: + if (tag !== 48) { + break; + } + + message.workflowRunLastStatus = reader.int32() as any; + continue; + case 7: + if (tag !== 56) { + break; + } + + message.workflowLastActivityWindow = reader.int32() as any; + continue; + case 8: + if (tag !== 66) { + break; + } + + message.pagination = OffsetPaginationRequest.decode(reader, reader.uint32()); continue; } if ((tag & 7) === 4 || tag === 0) { @@ -587,14 +736,39 @@ export const WorkflowServiceListRequest = { fromJSON(object: any): WorkflowServiceListRequest { return { - projectReference: isSet(object.projectReference) ? EntityRef.fromJSON(object.projectReference) : undefined, + workflowName: isSet(object.workflowName) ? String(object.workflowName) : "", + workflowTeam: isSet(object.workflowTeam) ? String(object.workflowTeam) : "", + projectNames: Array.isArray(object?.projectNames) ? object.projectNames.map((e: any) => String(e)) : [], + workflowPublic: isSet(object.workflowPublic) ? Boolean(object.workflowPublic) : undefined, + workflowRunRunnerType: isSet(object.workflowRunRunnerType) + ? craftingSchema_Runner_RunnerTypeFromJSON(object.workflowRunRunnerType) + : 0, + workflowRunLastStatus: isSet(object.workflowRunLastStatus) ? runStatusFromJSON(object.workflowRunLastStatus) : 0, + workflowLastActivityWindow: isSet(object.workflowLastActivityWindow) + ? workflowActivityWindowFromJSON(object.workflowLastActivityWindow) + : 0, + pagination: isSet(object.pagination) ? OffsetPaginationRequest.fromJSON(object.pagination) : undefined, }; }, toJSON(message: WorkflowServiceListRequest): unknown { const obj: any = {}; - message.projectReference !== undefined && - (obj.projectReference = message.projectReference ? EntityRef.toJSON(message.projectReference) : undefined); + message.workflowName !== undefined && (obj.workflowName = message.workflowName); + message.workflowTeam !== undefined && (obj.workflowTeam = message.workflowTeam); + if (message.projectNames) { + obj.projectNames = message.projectNames.map((e) => e); + } else { + obj.projectNames = []; + } + message.workflowPublic !== undefined && (obj.workflowPublic = message.workflowPublic); + message.workflowRunRunnerType !== undefined && + (obj.workflowRunRunnerType = craftingSchema_Runner_RunnerTypeToJSON(message.workflowRunRunnerType)); + message.workflowRunLastStatus !== undefined && + (obj.workflowRunLastStatus = runStatusToJSON(message.workflowRunLastStatus)); + message.workflowLastActivityWindow !== undefined && + (obj.workflowLastActivityWindow = workflowActivityWindowToJSON(message.workflowLastActivityWindow)); + message.pagination !== undefined && + (obj.pagination = message.pagination ? OffsetPaginationRequest.toJSON(message.pagination) : undefined); return obj; }, @@ -604,15 +778,22 @@ export const WorkflowServiceListRequest = { fromPartial, I>>(object: I): WorkflowServiceListRequest { const message = createBaseWorkflowServiceListRequest(); - message.projectReference = (object.projectReference !== undefined && object.projectReference !== null) - ? EntityRef.fromPartial(object.projectReference) + message.workflowName = object.workflowName ?? ""; + message.workflowTeam = object.workflowTeam ?? ""; + message.projectNames = object.projectNames?.map((e) => e) || []; + message.workflowPublic = object.workflowPublic ?? undefined; + message.workflowRunRunnerType = object.workflowRunRunnerType ?? 0; + message.workflowRunLastStatus = object.workflowRunLastStatus ?? 0; + message.workflowLastActivityWindow = object.workflowLastActivityWindow ?? 0; + message.pagination = (object.pagination !== undefined && object.pagination !== null) + ? OffsetPaginationRequest.fromPartial(object.pagination) : undefined; return message; }, }; function createBaseWorkflowServiceListResponse(): WorkflowServiceListResponse { - return { result: [] }; + return { result: [], pagination: undefined }; } export const WorkflowServiceListResponse = { @@ -620,6 +801,9 @@ export const WorkflowServiceListResponse = { for (const v of message.result) { WorkflowItem.encode(v!, writer.uint32(10).fork()).ldelim(); } + if (message.pagination !== undefined) { + OffsetPaginationResponse.encode(message.pagination, writer.uint32(18).fork()).ldelim(); + } return writer; }, @@ -637,6 +821,13 @@ export const WorkflowServiceListResponse = { message.result.push(WorkflowItem.decode(reader, reader.uint32())); continue; + case 2: + if (tag !== 18) { + break; + } + + message.pagination = OffsetPaginationResponse.decode(reader, reader.uint32()); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -647,7 +838,10 @@ export const WorkflowServiceListResponse = { }, fromJSON(object: any): WorkflowServiceListResponse { - return { result: Array.isArray(object?.result) ? object.result.map((e: any) => WorkflowItem.fromJSON(e)) : [] }; + return { + result: Array.isArray(object?.result) ? object.result.map((e: any) => WorkflowItem.fromJSON(e)) : [], + pagination: isSet(object.pagination) ? OffsetPaginationResponse.fromJSON(object.pagination) : undefined, + }; }, toJSON(message: WorkflowServiceListResponse): unknown { @@ -657,6 +851,8 @@ export const WorkflowServiceListResponse = { } else { obj.result = []; } + message.pagination !== undefined && + (obj.pagination = message.pagination ? OffsetPaginationResponse.toJSON(message.pagination) : undefined); return obj; }, @@ -667,6 +863,9 @@ export const WorkflowServiceListResponse = { fromPartial, I>>(object: I): WorkflowServiceListResponse { const message = createBaseWorkflowServiceListResponse(); message.result = object.result?.map((e) => WorkflowItem.fromPartial(e)) || []; + message.pagination = (object.pagination !== undefined && object.pagination !== null) + ? OffsetPaginationResponse.fromPartial(object.pagination) + : undefined; return message; }, }; diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationRequest.jsonschema.json new file mode 100644 index 000000000..2346c8ca5 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationRequest.jsonschema.json @@ -0,0 +1,30 @@ +{ + "$id": "controlplane.v1.OffsetPaginationRequest.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "OffsetPaginationRequest is used to paginate the results", + "patternProperties": { + "^(page_size)$": { + "description": "The maximum number of entries to return. If the value exceeds the maximum, then the maximum value will be used.", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + } + }, + "properties": { + "page": { + "description": "The (zero-based) offset of the first item returned in the collection.", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "pageSize": { + "description": "The maximum number of entries to return. If the value exceeds the maximum, then the maximum value will be used.", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + } + }, + "title": "Offset Pagination Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationRequest.schema.json new file mode 100644 index 000000000..3ab0b38b7 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationRequest.schema.json @@ -0,0 +1,30 @@ +{ + "$id": "controlplane.v1.OffsetPaginationRequest.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "OffsetPaginationRequest is used to paginate the results", + "patternProperties": { + "^(pageSize)$": { + "description": "The maximum number of entries to return. If the value exceeds the maximum, then the maximum value will be used.", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + } + }, + "properties": { + "page": { + "description": "The (zero-based) offset of the first item returned in the collection.", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "page_size": { + "description": "The maximum number of entries to return. If the value exceeds the maximum, then the maximum value will be used.", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + } + }, + "title": "Offset Pagination Request", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationResponse.jsonschema.json new file mode 100644 index 000000000..3da0e18e4 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationResponse.jsonschema.json @@ -0,0 +1,54 @@ +{ + "$id": "controlplane.v1.OffsetPaginationResponse.jsonschema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "OffsetPaginationResponse is used to return the pagination information", + "patternProperties": { + "^(page_size)$": { + "description": "The number of results per page", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "^(total_count)$": { + "description": "The total number of results", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "^(total_pages)$": { + "description": "The total number of pages", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + } + }, + "properties": { + "page": { + "description": "The current page number", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "pageSize": { + "description": "The number of results per page", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "totalCount": { + "description": "The total number of results", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "totalPages": { + "description": "The total number of pages", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + } + }, + "title": "Offset Pagination Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationResponse.schema.json new file mode 100644 index 000000000..8770ce711 --- /dev/null +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.OffsetPaginationResponse.schema.json @@ -0,0 +1,54 @@ +{ + "$id": "controlplane.v1.OffsetPaginationResponse.schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "OffsetPaginationResponse is used to return the pagination information", + "patternProperties": { + "^(pageSize)$": { + "description": "The number of results per page", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "^(totalCount)$": { + "description": "The total number of results", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "^(totalPages)$": { + "description": "The total number of pages", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + } + }, + "properties": { + "page": { + "description": "The current page number", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "page_size": { + "description": "The number of results per page", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "total_count": { + "description": "The total number of results", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + }, + "total_pages": { + "description": "The total number of pages", + "exclusiveMaximum": 2147483648, + "minimum": -2147483648, + "type": "integer" + } + }, + "title": "Offset Pagination Response", + "type": "object" +} diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListRequest.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListRequest.jsonschema.json index ae0400a1d..99beb58d2 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListRequest.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListRequest.jsonschema.json @@ -3,13 +3,179 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "patternProperties": { - "^(project_reference)$": { - "$ref": "controlplane.v1.EntityRef.jsonschema.json" + "^(project_names)$": { + "description": "The project the workflow belongs to", + "items": { + "type": "string" + }, + "type": "array" + }, + "^(workflow_last_activity_window)$": { + "anyOf": [ + { + "enum": [ + "WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED", + "WORKFLOW_ACTIVITY_WINDOW_LAST_DAY", + "WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS", + "WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS" + ], + "title": "Workflow Activity Window", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The time window for the last known workflow activity" + }, + "^(workflow_name)$": { + "description": "The name of the workflow to filter by", + "type": "string" + }, + "^(workflow_public)$": { + "description": "If the workflow is public", + "type": "boolean" + }, + "^(workflow_run_last_status)$": { + "anyOf": [ + { + "enum": [ + "RUN_STATUS_UNSPECIFIED", + "RUN_STATUS_INITIALIZED", + "RUN_STATUS_SUCCEEDED", + "RUN_STATUS_FAILED", + "RUN_STATUS_EXPIRED", + "RUN_STATUS_CANCELLED" + ], + "title": "Run Status", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The status of the last workflow run" + }, + "^(workflow_run_runner_type)$": { + "anyOf": [ + { + "enum": [ + "RUNNER_TYPE_UNSPECIFIED", + "GITHUB_ACTION", + "GITLAB_PIPELINE", + "AZURE_PIPELINE", + "JENKINS_JOB", + "CIRCLECI_BUILD", + "DAGGER_PIPELINE" + ], + "title": "Runner Type", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The type of runner that ran the workflow" + }, + "^(workflow_team)$": { + "description": "The team the workflow belongs to", + "type": "string" } }, "properties": { - "projectReference": { - "$ref": "controlplane.v1.EntityRef.jsonschema.json" + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationRequest.jsonschema.json", + "description": "Pagination options" + }, + "projectNames": { + "description": "The project the workflow belongs to", + "items": { + "type": "string" + }, + "type": "array" + }, + "workflowLastActivityWindow": { + "anyOf": [ + { + "enum": [ + "WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED", + "WORKFLOW_ACTIVITY_WINDOW_LAST_DAY", + "WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS", + "WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS" + ], + "title": "Workflow Activity Window", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The time window for the last known workflow activity" + }, + "workflowName": { + "description": "The name of the workflow to filter by", + "type": "string" + }, + "workflowPublic": { + "description": "If the workflow is public", + "type": "boolean" + }, + "workflowRunLastStatus": { + "anyOf": [ + { + "enum": [ + "RUN_STATUS_UNSPECIFIED", + "RUN_STATUS_INITIALIZED", + "RUN_STATUS_SUCCEEDED", + "RUN_STATUS_FAILED", + "RUN_STATUS_EXPIRED", + "RUN_STATUS_CANCELLED" + ], + "title": "Run Status", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The status of the last workflow run" + }, + "workflowRunRunnerType": { + "anyOf": [ + { + "enum": [ + "RUNNER_TYPE_UNSPECIFIED", + "GITHUB_ACTION", + "GITLAB_PIPELINE", + "AZURE_PIPELINE", + "JENKINS_JOB", + "CIRCLECI_BUILD", + "DAGGER_PIPELINE" + ], + "title": "Runner Type", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The type of runner that ran the workflow" + }, + "workflowTeam": { + "description": "The team the workflow belongs to", + "type": "string" } }, "title": "Workflow Service List Request", diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListRequest.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListRequest.schema.json index 2acf26375..57d2782ad 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListRequest.schema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListRequest.schema.json @@ -3,13 +3,179 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "patternProperties": { - "^(projectReference)$": { - "$ref": "controlplane.v1.EntityRef.schema.json" + "^(projectNames)$": { + "description": "The project the workflow belongs to", + "items": { + "type": "string" + }, + "type": "array" + }, + "^(workflowLastActivityWindow)$": { + "anyOf": [ + { + "enum": [ + "WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED", + "WORKFLOW_ACTIVITY_WINDOW_LAST_DAY", + "WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS", + "WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS" + ], + "title": "Workflow Activity Window", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The time window for the last known workflow activity" + }, + "^(workflowName)$": { + "description": "The name of the workflow to filter by", + "type": "string" + }, + "^(workflowPublic)$": { + "description": "If the workflow is public", + "type": "boolean" + }, + "^(workflowRunLastStatus)$": { + "anyOf": [ + { + "enum": [ + "RUN_STATUS_UNSPECIFIED", + "RUN_STATUS_INITIALIZED", + "RUN_STATUS_SUCCEEDED", + "RUN_STATUS_FAILED", + "RUN_STATUS_EXPIRED", + "RUN_STATUS_CANCELLED" + ], + "title": "Run Status", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The status of the last workflow run" + }, + "^(workflowRunRunnerType)$": { + "anyOf": [ + { + "enum": [ + "RUNNER_TYPE_UNSPECIFIED", + "GITHUB_ACTION", + "GITLAB_PIPELINE", + "AZURE_PIPELINE", + "JENKINS_JOB", + "CIRCLECI_BUILD", + "DAGGER_PIPELINE" + ], + "title": "Runner Type", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The type of runner that ran the workflow" + }, + "^(workflowTeam)$": { + "description": "The team the workflow belongs to", + "type": "string" } }, "properties": { - "project_reference": { - "$ref": "controlplane.v1.EntityRef.schema.json" + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationRequest.schema.json", + "description": "Pagination options" + }, + "project_names": { + "description": "The project the workflow belongs to", + "items": { + "type": "string" + }, + "type": "array" + }, + "workflow_last_activity_window": { + "anyOf": [ + { + "enum": [ + "WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED", + "WORKFLOW_ACTIVITY_WINDOW_LAST_DAY", + "WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS", + "WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS" + ], + "title": "Workflow Activity Window", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The time window for the last known workflow activity" + }, + "workflow_name": { + "description": "The name of the workflow to filter by", + "type": "string" + }, + "workflow_public": { + "description": "If the workflow is public", + "type": "boolean" + }, + "workflow_run_last_status": { + "anyOf": [ + { + "enum": [ + "RUN_STATUS_UNSPECIFIED", + "RUN_STATUS_INITIALIZED", + "RUN_STATUS_SUCCEEDED", + "RUN_STATUS_FAILED", + "RUN_STATUS_EXPIRED", + "RUN_STATUS_CANCELLED" + ], + "title": "Run Status", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The status of the last workflow run" + }, + "workflow_run_runner_type": { + "anyOf": [ + { + "enum": [ + "RUNNER_TYPE_UNSPECIFIED", + "GITHUB_ACTION", + "GITLAB_PIPELINE", + "AZURE_PIPELINE", + "JENKINS_JOB", + "CIRCLECI_BUILD", + "DAGGER_PIPELINE" + ], + "title": "Runner Type", + "type": "string" + }, + { + "maximum": 2147483647, + "minimum": -2147483648, + "type": "integer" + } + ], + "description": "The type of runner that ran the workflow" + }, + "workflow_team": { + "description": "The team the workflow belongs to", + "type": "string" } }, "title": "Workflow Service List Request", diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListResponse.jsonschema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListResponse.jsonschema.json index 0672df033..fda49160c 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListResponse.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListResponse.jsonschema.json @@ -3,6 +3,9 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "properties": { + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationResponse.jsonschema.json" + }, "result": { "items": { "$ref": "controlplane.v1.WorkflowItem.jsonschema.json" diff --git a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListResponse.schema.json b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListResponse.schema.json index d70280dfd..05b9ba2a3 100644 --- a/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListResponse.schema.json +++ b/app/controlplane/api/gen/jsonschema/controlplane.v1.WorkflowServiceListResponse.schema.json @@ -3,6 +3,9 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "properties": { + "pagination": { + "$ref": "controlplane.v1.OffsetPaginationResponse.schema.json" + }, "result": { "items": { "$ref": "controlplane.v1.WorkflowItem.schema.json" diff --git a/app/controlplane/internal/service/service.go b/app/controlplane/internal/service/service.go index 124dc0d92..8e2b3afbc 100644 --- a/app/controlplane/internal/service/service.go +++ b/app/controlplane/internal/service/service.go @@ -21,6 +21,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/internal/usercontext" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination" "github.com/chainloop-dev/chainloop/pkg/servicelogger" "github.com/go-kratos/kratos/v2/errors" "github.com/go-kratos/kratos/v2/log" @@ -141,7 +142,7 @@ func handleUseCaseErr(err error, l *log.Helper) error { switch { case errors.Is(err, context.Canceled): return errors.ClientClosed("client closed", err.Error()) - case biz.IsErrValidation(err) || biz.IsErrInvalidUUID(err) || biz.IsErrInvalidTimeWindow(err): + case biz.IsErrValidation(err) || biz.IsErrInvalidUUID(err) || biz.IsErrInvalidTimeWindow(err) || pagination.IsOffsetPaginationError(err): return errors.BadRequest("invalid", err.Error()) case biz.IsNotFound(err): return errors.NotFound("not found", err.Error()) diff --git a/app/controlplane/internal/service/workflow.go b/app/controlplane/internal/service/workflow.go index aeb78be81..73015a6e4 100644 --- a/app/controlplane/internal/service/workflow.go +++ b/app/controlplane/internal/service/workflow.go @@ -17,9 +17,15 @@ package service import ( "context" + "fmt" + "time" pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" + schema "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination" + + "github.com/go-kratos/kratos/v2/errors" "github.com/google/uuid" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -105,33 +111,75 @@ func (s *WorkflowService) Update(ctx context.Context, req *pb.WorkflowServiceUpd return &pb.WorkflowServiceUpdateResponse{Result: bizWorkflowToPb(p)}, nil } +// List returns a list of workflows. func (s *WorkflowService) List(ctx context.Context, req *pb.WorkflowServiceListRequest) (*pb.WorkflowServiceListResponse, error) { currentOrg, err := requireCurrentOrg(ctx) if err != nil { return nil, err } - // If project reference is provided, find the project and scope the workflow List request - // to only projects within the project - var projectID string - if req.GetProjectReference().GetRef() != nil { - pro, err := s.projectsUseCase.FindProjectByReference(ctx, currentOrg.ID, &biz.EntityRef{ - ID: req.GetProjectReference().GetEntityId(), - Name: req.GetProjectReference().GetEntityName(), - }) + // Initialize the pagination options, with default values + paginationOpts := pagination.NewDefaultOffsetPaginationOpts() + + // Override the pagination options if they are provided + if req.GetPagination() != nil { + paginationOpts, err = pagination.NewOffsetPaginationOpts( + int(req.GetPagination().GetPage()), + int(req.GetPagination().GetPageSize()), + ) if err != nil { return nil, handleUseCaseErr(err, s.log) } + } + + // Initialize the filters + filters := &biz.WorkflowListOpts{} + + // Check the workflow name + if req.GetWorkflowName() != "" { + filters.WorkflowName = req.GetWorkflowName() + } + + // Check the Team + if req.GetWorkflowTeam() != "" { + filters.WorkflowTeam = req.GetWorkflowTeam() + } + + // Check the Project Name + if len(req.GetProjectNames()) != 0 { + filters.WorkflowProjectNames = req.GetProjectNames() + } + + // Workflow visibility + if req.WorkflowPublic != nil { + val := req.GetWorkflowPublic() + filters.WorkflowPublic = &val + } + + // Workflow Run Runner Type + if req.GetWorkflowRunRunnerType() != schema.CraftingSchema_Runner_RUNNER_TYPE_UNSPECIFIED { + filters.WorkflowRunRunnerType = req.GetWorkflowRunRunnerType().String() + } - // If the project is not found, return an empty response - if pro == nil { - return &pb.WorkflowServiceListResponse{}, nil + // Workflow Last Known Status + if req.GetWorkflowRunLastStatus() != pb.RunStatus_RUN_STATUS_UNSPECIFIED { + status, err := pbWorkflowRunStatusToBiz(req.GetWorkflowRunLastStatus()) + if err != nil { + return nil, errors.BadRequest("invalid argument", err.Error()) } + filters.WorkflowRunLastStatus = status + } - projectID = pro.ID.String() + // Workflow Last Activity Window + if req.GetWorkflowLastActivityWindow() != pb.WorkflowActivityWindow_WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED { + timeWindow, err := workflowsActivityTimeWindowPbToTimeWindow(req.GetWorkflowLastActivityWindow()) + if err != nil { + return nil, errors.BadRequest("invalid argument", err.Error()) + } + filters.WorkflowActiveWindow = timeWindow } - workflows, err := s.useCase.List(ctx, currentOrg.ID, projectID) + workflows, count, err := s.useCase.List(ctx, currentOrg.ID, filters, paginationOpts) if err != nil { return nil, handleUseCaseErr(err, s.log) } @@ -141,7 +189,10 @@ func (s *WorkflowService) List(ctx context.Context, req *pb.WorkflowServiceListR result = append(result, bizWorkflowToPb(p)) } - return &pb.WorkflowServiceListResponse{Result: result}, nil + return &pb.WorkflowServiceListResponse{ + Result: result, + Pagination: paginationToPb(count, paginationOpts.Offset(), paginationOpts.Limit()), + }, nil } func (s *WorkflowService) Delete(ctx context.Context, req *pb.WorkflowServiceDeleteRequest) (*pb.WorkflowServiceDeleteResponse, error) { @@ -200,3 +251,32 @@ func bizWorkflowToPb(wf *biz.Workflow) *pb.WorkflowItem { func bizWorkflowRefToPb(wf *biz.WorkflowRef) *pb.WorkflowRef { return &pb.WorkflowRef{Id: wf.ID.String(), Name: wf.Name, ProjectName: wf.ProjectName} } + +// workflowsActivityTimeWindowPbToTimeWindow converts a v1.WorkflowActivityWindow to a biz.TimeWindow. +func workflowsActivityTimeWindowPbToTimeWindow(tw pb.WorkflowActivityWindow) (*biz.TimeWindow, error) { + timeWindow := &biz.TimeWindow{ + To: time.Now().UTC(), + } + switch tw { + case pb.WorkflowActivityWindow_WORKFLOW_ACTIVITY_WINDOW_LAST_DAY: + timeWindow.From = timeWindow.To.Add(-24 * time.Hour) + case pb.WorkflowActivityWindow_WORKFLOW_ACTIVITY_WINDOW_LAST_7_DAYS: + timeWindow.From = timeWindow.To.Add(-7 * 24 * time.Hour) + case pb.WorkflowActivityWindow_WORKFLOW_ACTIVITY_WINDOW_LAST_30_DAYS: + timeWindow.From = timeWindow.To.Add(-30 * 24 * time.Hour) + case pb.WorkflowActivityWindow_WORKFLOW_ACTIVITY_WINDOW_UNSPECIFIED: + return nil, fmt.Errorf("invalid time window: %s", tw) + } + + return timeWindow, nil +} + +// paginationToPb converts a count, offset, and limit to a pb.OffsetPaginationResponse. +func paginationToPb(count, offset, limit int) *pb.OffsetPaginationResponse { + return &pb.OffsetPaginationResponse{ + TotalCount: int32(count), + Page: int32(offset/limit) + 1, + TotalPages: (int32(count) + int32(limit) - 1) / int32(limit), + PageSize: int32(limit), + } +} diff --git a/app/controlplane/pkg/biz/organization_integration_test.go b/app/controlplane/pkg/biz/organization_integration_test.go index 967894c1e..95028b0f8 100644 --- a/app/controlplane/pkg/biz/organization_integration_test.go +++ b/app/controlplane/pkg/biz/organization_integration_test.go @@ -145,7 +145,7 @@ func (s *OrgIntegrationTestSuite) TestDeleteOrg() { assert.Nil(ociRepo) assert.ErrorAs(err, &biz.ErrNotFound{}) - workflows, err := s.Workflow.List(ctx, s.org.ID, "") + workflows, _, err := s.Workflow.List(ctx, s.org.ID, nil, nil) assert.NoError(err) assert.Empty(workflows) @@ -226,7 +226,7 @@ func (s *OrgIntegrationTestSuite) SetupTest() { assert.NoError(err) assert.NotNil(ociRepo) - workflows, err := s.Workflow.List(ctx, s.org.ID, "") + workflows, _, err := s.Workflow.List(ctx, s.org.ID, nil, nil) assert.NoError(err) assert.Len(workflows, 1) diff --git a/app/controlplane/pkg/biz/orgmetrics.go b/app/controlplane/pkg/biz/orgmetrics.go index 42ee7cf3a..aa7f38d2f 100644 --- a/app/controlplane/pkg/biz/orgmetrics.go +++ b/app/controlplane/pkg/biz/orgmetrics.go @@ -21,6 +21,7 @@ import ( "time" prometheuscollector "github.com/chainloop-dev/chainloop/app/controlplane/pkg/metrics/prometheus/collector" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination" "github.com/go-kratos/kratos/v2/log" "github.com/google/uuid" @@ -169,10 +170,34 @@ func (uc *OrgMetricsUseCase) GetLastWorkflowStatusByRun(ctx context.Context, org return nil, fmt.Errorf("finding organization: %w", err) } - // List all workflows - wfs, err := uc.wfUseCase.List(ctx, org.ID, "") + // Default pagination option + paginationOpts, err := pagination.NewOffsetPaginationOpts(pagination.DefaultPage, 100) if err != nil { - return nil, fmt.Errorf("listing workflows: %w", err) + return nil, fmt.Errorf("creating pagination options: %w", err) + } + + var wfs []*Workflow + // Request all workflows using the pagination until the returned workflows are less than the limit + for { + // List all workflows + wfsPage, _, err := uc.wfUseCase.List(ctx, org.ID, nil, paginationOpts) + if err != nil { + return nil, fmt.Errorf("listing workflows: %w", err) + } + + // Append workflows to the list + wfs = append(wfs, wfsPage...) + + // Check if there are more workflows to fetch + if len(wfsPage) < paginationOpts.Limit() { + break + } + + // Update pagination options with the next offset + paginationOpts, err = pagination.NewOffsetPaginationOpts(paginationOpts.Offset()+paginationOpts.Limit(), paginationOpts.Limit()) + if err != nil { + return nil, fmt.Errorf("creating pagination options: %w", err) + } } // Create reports diff --git a/app/controlplane/pkg/biz/orgmetrics_integration_test.go b/app/controlplane/pkg/biz/orgmetrics_integration_test.go new file mode 100644 index 000000000..096c27354 --- /dev/null +++ b/app/controlplane/pkg/biz/orgmetrics_integration_test.go @@ -0,0 +1,111 @@ +// +// Copyright 2024 The Chainloop Authors. +// +// 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 biz_test + +import ( + "context" + "testing" + + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz/testhelpers" + "github.com/chainloop-dev/chainloop/pkg/credentials" + creds "github.com/chainloop-dev/chainloop/pkg/credentials/mocks" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/suite" +) + +func TestOrgMetricsUseCase(t *testing.T) { + suite.Run(t, new(orgMetricsGetLastWorkflowStatusByRunTestSuite)) +} + +type orgMetricsGetLastWorkflowStatusByRunTestSuite struct { + testhelpers.UseCasesEachTestSuite + org *biz.Organization +} + +func (s *orgMetricsGetLastWorkflowStatusByRunTestSuite) TearDownSubTest() { + _, _ = s.Data.DB.Project.Delete().Exec(context.Background()) + _, _ = s.Data.DB.Workflow.Delete().Exec(context.Background()) + _, _ = s.Data.DB.WorkflowRun.Delete().Exec(context.Background()) +} + +func (s *orgMetricsGetLastWorkflowStatusByRunTestSuite) SetupTest() { + var err error + assert := assert.New(s.T()) + + credsWriter := creds.NewReaderWriter(s.T()) + credsWriter.On("SaveCredentials", context.Background(), mock.Anything, &credentials.OCIKeypair{Repo: "repo", Username: "username", Password: "pass"}).Return("stored-OCI-secret", nil) + + s.TestingUseCases = testhelpers.NewTestingUseCases(s.T(), testhelpers.WithCredsReaderWriter(credsWriter)) + + ctx := context.Background() + s.org, err = s.Organization.CreateWithRandomName(ctx) + assert.NoError(err) +} + +func (s *orgMetricsGetLastWorkflowStatusByRunTestSuite) TestGetLastWorkflowStatusByRun() { + s.Run("no workflow runs", func() { + ctx := context.Background() + wfs, err := s.TestingUseCases.OrgMetrics.GetLastWorkflowStatusByRun(ctx, s.org.Name) + assert.NoError(s.T(), err) + assert.Len(s.T(), wfs, 0) + }) + + s.Run("one workflow with no runs", func() { + ctx := context.Background() + _, err := s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{Description: description, Name: "the-name", Team: "the-team", Project: "the-project", OrgID: s.org.ID}) + assert.NoError(s.T(), err) + + _, err = s.TestingUseCases.OrgMetrics.GetLastWorkflowStatusByRun(ctx, s.org.Name) + assert.NoError(s.T(), err) + }) + + s.Run("one workflow with one run", func() { + ctx := context.Background() + w, err := s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{Description: description, Name: "the-name", Team: "the-team", Project: "the-project", OrgID: s.org.ID}) + assert.NoError(s.T(), err) + + // Find contract revision + contractVersion, err := s.TestingUseCases.WorkflowContract.Describe(ctx, s.org.ID, w.ContractID.String(), 0) + assert.NoError(s.T(), err) + assert.NotNil(s.T(), contractVersion) + + // Create a CAS backend + casBackend, err := s.TestingUseCases.CASBackend.CreateOrUpdate(ctx, s.org.ID, "repo", "username", "pass", backendType, true) + assert.NoError(s.T(), err) + assert.NotNil(s.T(), casBackend) + + // Create the workflow run + wfRun, err := s.TestingUseCases.WorkflowRun.Create(ctx, + &biz.WorkflowRunCreateOpts{ + WorkflowID: w.ID.String(), ContractRevision: contractVersion, CASBackendID: casBackend.ID, + ProjectVersion: version1, + }) + assert.NoError(s.T(), err) + assert.NotNil(s.T(), wfRun) + + // Update the workflow run + err = s.TestingUseCases.WorkflowRun.MarkAsFinished(ctx, wfRun.ID.String(), biz.WorkflowRunSuccess, "the-logs") + assert.NoError(s.T(), err) + + wfs, err := s.TestingUseCases.OrgMetrics.GetLastWorkflowStatusByRun(ctx, s.org.Name) + assert.NoError(s.T(), err) + assert.Len(s.T(), wfs, 1) + assert.Equal(s.T(), "success", wfs[0].Status) + }) +} diff --git a/app/controlplane/pkg/biz/testhelpers/database.go b/app/controlplane/pkg/biz/testhelpers/database.go index 257833d19..00c45e97d 100644 --- a/app/controlplane/pkg/biz/testhelpers/database.go +++ b/app/controlplane/pkg/biz/testhelpers/database.go @@ -75,6 +75,7 @@ type TestingUseCases struct { AttestationState *biz.AttestationStateUseCase ProjectVersion *biz.ProjectVersionUseCase Project *biz.ProjectUseCase + OrgMetrics *biz.OrgMetricsUseCase // Repositories that can be used for custom crafting of use-cases Repos *TestingRepos } diff --git a/app/controlplane/pkg/biz/testhelpers/wire_gen.go b/app/controlplane/pkg/biz/testhelpers/wire_gen.go index d0c985c00..8369475a1 100644 --- a/app/controlplane/pkg/biz/testhelpers/wire_gen.go +++ b/app/controlplane/pkg/biz/testhelpers/wire_gen.go @@ -154,6 +154,7 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r AttestationState: attestationStateUseCase, ProjectVersion: projectVersionUseCase, Project: projectUseCase, + OrgMetrics: orgMetricsUseCase, Repos: testingRepos, } return testingUseCases, func() { diff --git a/app/controlplane/pkg/biz/workflow.go b/app/controlplane/pkg/biz/workflow.go index cca698e0b..e3b768ad1 100644 --- a/app/controlplane/pkg/biz/workflow.go +++ b/app/controlplane/pkg/biz/workflow.go @@ -21,6 +21,8 @@ import ( "fmt" "time" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination" + "github.com/go-kratos/kratos/v2/log" "github.com/google/uuid" ) @@ -52,7 +54,7 @@ type WorkflowRef struct { type WorkflowRepo interface { Create(ctx context.Context, opts *WorkflowCreateOpts) (*Workflow, error) Update(ctx context.Context, id uuid.UUID, opts *WorkflowUpdateOpts) (*Workflow, error) - List(ctx context.Context, orgID uuid.UUID, projectID uuid.UUID) ([]*Workflow, error) + List(ctx context.Context, orgID uuid.UUID, filter *WorkflowListOpts, pagination *pagination.OffsetPaginationOpts) ([]*Workflow, int, error) GetOrgScoped(ctx context.Context, orgID, workflowID uuid.UUID) (*Workflow, error) GetOrgScopedByProjectAndName(ctx context.Context, orgID uuid.UUID, projectName, workflowName string) (*Workflow, error) IncRunsCounter(ctx context.Context, workflowID uuid.UUID) error @@ -74,6 +76,26 @@ type WorkflowUpdateOpts struct { Public *bool } +// WorkflowListOpts is the options to filter the list of workflows +type WorkflowListOpts struct { + // WorkflowName is the name of the workflow + WorkflowName string + // WorkflowDescription is the description of the workflow + WorkflowDescription string + // WorkflowTeam is the team of the workflow + WorkflowTeam string + // WorkflowProjectNames is the project name of the workflow + WorkflowProjectNames []string + // WorkflowPublic is the flag to filter public workflows + WorkflowPublic *bool + // WorkflowActiveWindow is the active window of the workflow + WorkflowRunRunnerType string + // WorkflowActiveWindow is the active window of the workflow + WorkflowActiveWindow *TimeWindow + // WorkflowRunStatus is the status of the workflow runs to return + WorkflowRunLastStatus WorkflowRunStatus +} + type WorkflowUseCase struct { wfRepo WorkflowRepo projectRepo ProjectsRepo @@ -180,22 +202,19 @@ func (uc *WorkflowUseCase) findOrCreateContract(ctx context.Context, orgID, name return uc.contractUC.Create(ctx, &WorkflowContractCreateOpts{OrgID: orgID, Name: name, AddUniquePrefix: true}) } -func (uc *WorkflowUseCase) List(ctx context.Context, orgID string, projectID string) ([]*Workflow, error) { +// List returns a list of workflows and the total count of workflows +func (uc *WorkflowUseCase) List(ctx context.Context, orgID string, filterOpts *WorkflowListOpts, paginationOpts *pagination.OffsetPaginationOpts) ([]*Workflow, int, error) { orgUUID, err := uuid.Parse(orgID) if err != nil { - return nil, NewErrInvalidUUID(err) + return nil, 0, NewErrInvalidUUID(err) } - - var projectUUID uuid.UUID - if projectID != "" { - parsedUUID, err := uuid.Parse(projectID) - if err != nil { - return nil, NewErrInvalidUUID(err) - } - projectUUID = parsedUUID + // Apply default pagination if not provided + pgOpts := pagination.NewDefaultOffsetPaginationOpts() + if paginationOpts != nil { + pgOpts = paginationOpts } - return uc.wfRepo.List(ctx, orgUUID, projectUUID) + return uc.wfRepo.List(ctx, orgUUID, filterOpts, pgOpts) } func (uc *WorkflowUseCase) IncRunsCounter(ctx context.Context, workflowID string) error { diff --git a/app/controlplane/pkg/biz/workflow_integration_test.go b/app/controlplane/pkg/biz/workflow_integration_test.go index fdc41a40a..6ebedd613 100644 --- a/app/controlplane/pkg/biz/workflow_integration_test.go +++ b/app/controlplane/pkg/biz/workflow_integration_test.go @@ -21,13 +21,18 @@ import ( "testing" "time" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination" + v1 "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz/testhelpers" + "github.com/chainloop-dev/chainloop/pkg/credentials" + creds "github.com/chainloop-dev/chainloop/pkg/credentials/mocks" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/google/uuid" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" ) @@ -319,9 +324,127 @@ func (s *workflowIntegrationTestSuite) TestUpdate() { } } +func (s *workflowListIntegrationTestSuite) TestList() { + ctx := context.Background() + const project = "project" + const team = "team" + const description = "description" + + s.Run("no workflows", func() { + workflows, _, err := s.Workflow.List(ctx, s.org.ID, nil, nil) + s.NoError(err) + s.Empty(workflows) + }) + + s.Run("list workflows without filters and pagination", func() { + _, err := s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name1", Project: project, Team: team, Description: description}) + require.NoError(s.T(), err) + _, err = s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name2", Project: project, Team: team, Description: description}) + require.NoError(s.T(), err) + + workflows, _, err := s.Workflow.List(ctx, s.org.ID, nil, nil) + s.NoError(err) + s.Len(workflows, 2) + }) + + s.Run("list workflows with workflow name filter", func() { + _, err := s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name1", Project: project, Team: team, Description: description}) + require.NoError(s.T(), err) + _, err = s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name2", Project: project, Team: team, Description: description}) + require.NoError(s.T(), err) + + workflows, _, err := s.Workflow.List(ctx, s.org.ID, &biz.WorkflowListOpts{WorkflowName: "name1"}, nil) + s.NoError(err) + s.Len(workflows, 1) + }) + + s.Run("list workflows with workflow team filter", func() { + _, err := s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name1", Project: project, Team: team, Description: description}) + require.NoError(s.T(), err) + _, err = s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name2", Project: project, Team: "other-team", Description: description}) + require.NoError(s.T(), err) + + workflows, _, err := s.Workflow.List(ctx, s.org.ID, &biz.WorkflowListOpts{WorkflowTeam: team}, nil) + s.NoError(err) + s.Len(workflows, 2) + }) + + s.Run("list workflows with workflow project name filter", func() { + _, err := s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name1", Project: project, Team: team, Description: description}) + require.NoError(s.T(), err) + _, err = s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name2", Project: "other-project", Team: team, Description: description}) + require.NoError(s.T(), err) + + workflows, _, err := s.Workflow.List(ctx, s.org.ID, &biz.WorkflowListOpts{WorkflowProjectNames: []string{"other-project"}}, nil) + s.NoError(err) + s.Len(workflows, 1) + }) + + s.Run("list workflows with workflow public filter", func() { + _, err := s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name1", Project: project, Team: team, Description: description, Public: true}) + require.NoError(s.T(), err) + _, err = s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name2", Project: project, Team: team, Description: description, Public: false}) + require.NoError(s.T(), err) + + workflows, _, err := s.Workflow.List(ctx, s.org.ID, &biz.WorkflowListOpts{WorkflowPublic: toPtrBool(true)}, nil) + s.NoError(err) + s.Len(workflows, 1) + }) + + s.Run("list workflows with workflow run runner type filter", func() { + _, err := s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name1", Project: project, Team: team, Description: description}) + require.NoError(s.T(), err) + w, err := s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name2", Project: project, Team: team, Description: description}) + require.NoError(s.T(), err) + + // Find contract revision + contractVersion, err := s.TestingUseCases.WorkflowContract.Describe(ctx, s.org.ID, w.ContractID.String(), 0) + assert.NoError(s.T(), err) + assert.NotNil(s.T(), contractVersion) + + // Create a CAS backend + casBackend, err := s.TestingUseCases.CASBackend.CreateOrUpdate(ctx, s.org.ID, "repo", "username", "pass", backendType, true) + assert.NoError(s.T(), err) + assert.NotNil(s.T(), casBackend) + + // Create the workflow run + wfRun, err := s.TestingUseCases.WorkflowRun.Create(ctx, + &biz.WorkflowRunCreateOpts{ + WorkflowID: w.ID.String(), ContractRevision: contractVersion, CASBackendID: casBackend.ID, + ProjectVersion: version1, RunnerType: "GITHUB_ACTION", + }) + assert.NoError(s.T(), err) + assert.NotNil(s.T(), wfRun) + + // Update the workflow run + err = s.TestingUseCases.WorkflowRun.MarkAsFinished(ctx, wfRun.ID.String(), biz.WorkflowRunSuccess, "the-logs") + assert.NoError(s.T(), err) + + workflows, _, err := s.Workflow.List(ctx, s.org.ID, &biz.WorkflowListOpts{WorkflowRunRunnerType: "GITHUB_ACTION"}, nil) + s.NoError(err) + s.Len(workflows, 1) + }) + + s.Run("list workflow with pagination", func() { + _, err := s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name1", Project: project, Team: team, Description: description}) + require.NoError(s.T(), err) + _, err = s.Workflow.Create(ctx, &biz.WorkflowCreateOpts{OrgID: s.org.ID, Name: "name2", Project: project, Team: team, Description: description}) + require.NoError(s.T(), err) + + paginationOpts, err := pagination.NewOffsetPaginationOpts(0, 1) + require.NoError(s.T(), err) + + workflows, count, err := s.Workflow.List(ctx, s.org.ID, nil, paginationOpts) + s.NoError(err) + s.Len(workflows, 1) + s.Equal(2, count) + }) +} + // Run the tests func TestWorkflowUseCase(t *testing.T) { suite.Run(t, new(workflowIntegrationTestSuite)) + suite.Run(t, new(workflowListIntegrationTestSuite)) } // Utility struct to hold the test suite @@ -348,6 +471,33 @@ func (s *workflowIntegrationTestSuite) SetupTest() { assert.NoError(err) } +// Utility struct to hold the test suite +type workflowListIntegrationTestSuite struct { + testhelpers.UseCasesEachTestSuite + org *biz.Organization +} + +func (s *workflowListIntegrationTestSuite) SetupTest() { + var err error + assert := assert.New(s.T()) + + credsWriter := creds.NewReaderWriter(s.T()) + credsWriter.On("SaveCredentials", context.Background(), mock.Anything, &credentials.OCIKeypair{Repo: "repo", Username: "username", Password: "pass"}).Return("stored-OCI-secret", nil) + + s.TestingUseCases = testhelpers.NewTestingUseCases(s.T(), testhelpers.WithCredsReaderWriter(credsWriter)) + + ctx := context.Background() + s.org, err = s.Organization.CreateWithRandomName(ctx) + assert.NoError(err) +} + +func (s *workflowListIntegrationTestSuite) TearDownSubTest() { + _, _ = s.Data.DB.Workflow.Delete().Exec(context.Background()) + _, _ = s.Data.DB.Project.Delete().Exec(context.Background()) + _, _ = s.Data.DB.WorkflowRun.Delete().Exec(context.Background()) + _, _ = s.Data.DB.CASBackend.Delete().Exec(context.Background()) +} + func toPtrS(s string) *string { return &s } diff --git a/app/controlplane/pkg/data/workflow.go b/app/controlplane/pkg/data/workflow.go index 6b5bb18a2..b99bb775f 100644 --- a/app/controlplane/pkg/data/workflow.go +++ b/app/controlplane/pkg/data/workflow.go @@ -20,6 +20,10 @@ import ( "fmt" "time" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/predicate" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/workflowrun" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/pagination" + "entgo.io/ent/dialect/sql" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent" @@ -138,36 +142,115 @@ func (r *WorkflowRepo) Update(ctx context.Context, id uuid.UUID, opts *biz.Workf return r.FindByID(ctx, wf.ID) } -func (r *WorkflowRepo) List(ctx context.Context, orgID uuid.UUID, projectID uuid.UUID) ([]*biz.Workflow, error) { - baseQuery := orgScopedQuery(r.data.DB, orgID). - QueryWorkflows(). - Where(workflow.DeletedAtIsNil()). - WithContract(). - WithOrganization(). - WithLatestWorkflowRun() +func (r *WorkflowRepo) List(ctx context.Context, orgID uuid.UUID, filter *biz.WorkflowListOpts, pagination *pagination.OffsetPaginationOpts) ([]*biz.Workflow, int, error) { + if pagination == nil { + return nil, 0, fmt.Errorf("pagination options is required") + } + + // Initialize the base query for WorkflowRun + baseQuery := orgScopedQuery(r.data.DB, orgID).QueryWorkflows() + + // Apply filters to the WorkflowRun query based on the provided options + baseQuery = applyWorkflowRunFilters(baseQuery, filter) - if projectID != uuid.Nil { - baseQuery = baseQuery.Where(workflow.ProjectID(projectID)) + // Initialize the Workflow query and apply organization and deletion filters + wfQuery := baseQuery.Where(workflow.DeletedAtIsNil()) + + // Apply additional filters to the Workflow query based on the provided options + wfQuery = applyWorkflowFilters(wfQuery, filter) + + // Get the count of all filtered rows without the limit and offset + count, err := wfQuery.Count(ctx) + if err != nil { + return nil, 0, err } - workflows, err := baseQuery. + // Apply pagination options and execute the query + workflows, err := wfQuery. + WithLatestWorkflowRun(). Order(ent.Desc(workflow.FieldCreatedAt)). + Limit(pagination.Limit()). + Offset(pagination.Offset()). All(ctx) if err != nil { - return nil, err + return nil, 0, err } result := make([]*biz.Workflow, 0, len(workflows)) for _, wf := range workflows { r, err := entWFToBizWF(ctx, wf) if err != nil { - return nil, fmt.Errorf("converting entity: %w", err) + return nil, 0, fmt.Errorf("converting entity: %w", err) } result = append(result, r) } - return result, nil + return result, count, nil +} + +// applyWorkflowRunFilters applies filters to the WorkflowRun query based on the provided options +func applyWorkflowRunFilters(baseQuery *ent.WorkflowQuery, opts *biz.WorkflowListOpts) *ent.WorkflowQuery { + if opts == nil || opts.WorkflowRunRunnerType == "" && opts.WorkflowRunLastStatus == "" { + return baseQuery + } + + query := baseQuery.QueryLatestWorkflowRun() + + if opts.WorkflowRunRunnerType != "" { + query = query.Where( + workflowrun.RunnerType(opts.WorkflowRunRunnerType), + ) + } + + if opts.WorkflowRunLastStatus != "" { + query = query.Where( + workflowrun.StateEQ(opts.WorkflowRunLastStatus), + ) + } + + return query.QueryWorkflow() +} + +// applyWorkflowFilters applies filters to the Workflow query based on the provided options +func applyWorkflowFilters(wfQuery *ent.WorkflowQuery, opts *biz.WorkflowListOpts) *ent.WorkflowQuery { + if opts != nil { + if opts.WorkflowPublic != nil { + wfQuery = wfQuery.Where(workflow.Public(*opts.WorkflowPublic)) + } + + // Updated at on Workflows is only updated when a new workflow run is referenced meaning + // a workflow run is started + if opts.WorkflowActiveWindow != nil { + wfQuery = wfQuery.Where( + workflow.UpdatedAtGTE(opts.WorkflowActiveWindow.From), + workflow.UpdatedAtLTE(opts.WorkflowActiveWindow.To), + ) + } + + if opts.WorkflowDescription != "" { + wfQuery = wfQuery.Where(workflow.DescriptionContains(opts.WorkflowDescription)) + } + + if len(opts.WorkflowProjectNames) != 0 { + wfQuery = wfQuery.Where(workflow.HasProjectWith(project.NameIn(opts.WorkflowProjectNames...))) + } + + // Combine WorkflowTeam and WorkflowName filters using OR logic + var orConditions []predicate.Workflow + if opts.WorkflowTeam != "" { + orConditions = append(orConditions, workflow.TeamContains(opts.WorkflowTeam)) + } + if opts.WorkflowName != "" { + orConditions = append(orConditions, workflow.NameContains(opts.WorkflowName)) + } + + if len(orConditions) > 0 { + wfQuery = wfQuery.Where(workflow.Or(orConditions...)) + } + } + + return wfQuery } // GetOrgScoped Gets a workflow making sure it belongs to a given org diff --git a/app/controlplane/pkg/pagination/offset.go b/app/controlplane/pkg/pagination/offset.go new file mode 100644 index 000000000..1393a3648 --- /dev/null +++ b/app/controlplane/pkg/pagination/offset.go @@ -0,0 +1,97 @@ +// +// Copyright 2024 The Chainloop Authors. +// +// 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 pagination + +import ( + "errors" +) + +const ( + // DefaultPage defines the default page number + DefaultPage = 1 + // DefaultPageSize defines the default number of items per page + DefaultPageSize = 15 +) + +// OffsetPaginationError is the error type for page-based pagination +type OffsetPaginationError struct { + err error +} + +// NewOffsetPaginationErrorStr creates a new OffsetPaginationError with the provided error message +func NewOffsetPaginationErrorStr(errMsg string) OffsetPaginationError { + return OffsetPaginationError{errors.New(errMsg)} +} + +// Error returns the error message +func (e OffsetPaginationError) Error() string { + return e.err.Error() +} + +// IsOffsetPaginationError checks if the error is an OffsetPaginationError +func IsOffsetPaginationError(err error) bool { + return errors.As(err, &OffsetPaginationError{}) +} + +// Unwrap returns the wrapped error +func (e OffsetPaginationError) Unwrap() error { + return e.err +} + +// OffsetPaginationOpts is the options for page-based pagination +type OffsetPaginationOpts struct { + // page is the page number + page int + // pageSize is the number of items per page + pageSize int +} + +// NewDefaultOffsetPaginationOpts creates a new OffsetPaginationOpts with default values +func NewDefaultOffsetPaginationOpts() *OffsetPaginationOpts { + return &OffsetPaginationOpts{ + page: DefaultPage, + pageSize: DefaultPageSize, + } +} + +// Offset returns the page number +func (o *OffsetPaginationOpts) Offset() int { + if o.page < 1 { + o.page = 1 + } + return (o.page - 1) * o.pageSize +} + +// Limit returns the number of items per page +func (o *OffsetPaginationOpts) Limit() int { + return o.pageSize +} + +// NewOffsetPaginationOpts creates a new OffsetPaginationOpts with the provided page and pageSize +func NewOffsetPaginationOpts(offset, limit int) (*OffsetPaginationOpts, error) { + if limit < 1 { + return nil, NewOffsetPaginationErrorStr("pageSize must be greater than 0") + } + + if offset < 0 { + return nil, NewOffsetPaginationErrorStr("page must be greater than or equal to 0") + } + + return &OffsetPaginationOpts{ + page: offset, + pageSize: limit, + }, nil +}