diff --git a/e2e_tests/tests/cluster/test_webhooks.py b/e2e_tests/tests/cluster/test_webhooks.py index 18a55bafd58..da82bcb6f3a 100644 --- a/e2e_tests/tests/cluster/test_webhooks.py +++ b/e2e_tests/tests/cluster/test_webhooks.py @@ -26,6 +26,9 @@ def test_slack_webhook() -> None: url=f"http://localhost:{port}", webhookType=bindings.v1WebhookType.SLACK, triggers=[webhook_trigger], + mode=bindings.v1WebhookMode.WORKSPACE, + name="", + workspaceId=None, ) result = bindings.post_PostWebhook(sess, body=webhook_request) @@ -103,6 +106,9 @@ def test_log_pattern_send_webhook(should_match: bool) -> None: url=f"http://localhost:{port}{slack_path}", webhookType=bindings.v1WebhookType.SLACK, triggers=[webhook_trigger], + mode=bindings.v1WebhookMode.WORKSPACE, + name="", + workspaceId=None, ), ) @@ -113,6 +119,9 @@ def test_log_pattern_send_webhook(should_match: bool) -> None: url=f"http://localhost:{port}{default_path}", webhookType=bindings.v1WebhookType.DEFAULT, triggers=[webhook_trigger], + mode=bindings.v1WebhookMode.WORKSPACE, + name="", + workspaceId=None, ), ) diff --git a/harness/determined/common/api/bindings.py b/harness/determined/common/api/bindings.py index 4e252806ac8..48b20f3cc41 100644 --- a/harness/determined/common/api/bindings.py +++ b/harness/determined/common/api/bindings.py @@ -16938,25 +16938,35 @@ def to_json(self, omit_unset: bool = False) -> typing.Dict[str, typing.Any]: class v1Webhook(Printable): id: "typing.Optional[int]" = None triggers: "typing.Optional[typing.Sequence[v1Trigger]]" = None + workspaceId: "typing.Optional[int]" = None def __init__( self, *, + mode: "v1WebhookMode", + name: str, url: str, webhookType: "v1WebhookType", id: "typing.Union[int, None, Unset]" = _unset, triggers: "typing.Union[typing.Sequence[v1Trigger], None, Unset]" = _unset, + workspaceId: "typing.Union[int, None, Unset]" = _unset, ): + self.mode = mode + self.name = name self.url = url self.webhookType = webhookType if not isinstance(id, Unset): self.id = id if not isinstance(triggers, Unset): self.triggers = triggers + if not isinstance(workspaceId, Unset): + self.workspaceId = workspaceId @classmethod def from_json(cls, obj: Json) -> "v1Webhook": kwargs: "typing.Dict[str, typing.Any]" = { + "mode": v1WebhookMode(obj["mode"]), + "name": obj["name"], "url": obj["url"], "webhookType": v1WebhookType(obj["webhookType"]), } @@ -16964,10 +16974,14 @@ def from_json(cls, obj: Json) -> "v1Webhook": kwargs["id"] = obj["id"] if "triggers" in obj: kwargs["triggers"] = [v1Trigger.from_json(x) for x in obj["triggers"]] if obj["triggers"] is not None else None + if "workspaceId" in obj: + kwargs["workspaceId"] = obj["workspaceId"] return cls(**kwargs) def to_json(self, omit_unset: bool = False) -> typing.Dict[str, typing.Any]: out: "typing.Dict[str, typing.Any]" = { + "mode": self.mode.value, + "name": self.name, "url": self.url, "webhookType": self.webhookType.value, } @@ -16975,8 +16989,21 @@ def to_json(self, omit_unset: bool = False) -> typing.Dict[str, typing.Any]: out["id"] = self.id if not omit_unset or "triggers" in vars(self): out["triggers"] = None if self.triggers is None else [x.to_json(omit_unset) for x in self.triggers] + if not omit_unset or "workspaceId" in vars(self): + out["workspaceId"] = self.workspaceId return out +class v1WebhookMode(DetEnum): + """Enum values for webhook mode. + - WEBHOOK_MODE_UNSPECIFIED: Default value + - WEBHOOK_MODE_WORKSPACE: Webhook will be triggered by all experiment in the workspace + - WEBHOOK_MODE_SPECIFIC: Webhook will only be triggered by experiment with matching configuration in + the same workspace as the web hook + """ + UNSPECIFIED = "WEBHOOK_MODE_UNSPECIFIED" + WORKSPACE = "WEBHOOK_MODE_WORKSPACE" + SPECIFIC = "WEBHOOK_MODE_SPECIFIC" + class v1WebhookType(DetEnum): """Enum values for expected webhook types. - WEBHOOK_TYPE_UNSPECIFIED: Default value diff --git a/master/internal/webhooks/postgres_webhook_intg_test.go b/master/internal/webhooks/postgres_webhook_intg_test.go index 871eeb06395..8b28d5f912c 100644 --- a/master/internal/webhooks/postgres_webhook_intg_test.go +++ b/master/internal/webhooks/postgres_webhook_intg_test.go @@ -78,9 +78,19 @@ func TestWebhooks(t *testing.T) { }) t.Run("webhook creation should work", func(t *testing.T) { + workspaceID := int32(1) testWebhookOne.Triggers = testTriggersOne + testWebhookOne.Mode = WebhookModeSpecific + testWebhookOne.Name = "test-name" + testWebhookOne.WorkspaceID = &workspaceID err := AddWebhook(ctx, &testWebhookOne) require.NoError(t, err, "failed to create webhook") + webhooks, err := GetWebhooks(ctx) + require.NoError(t, err, "unable to get webhooks") + webhookOneResponse := getWebhookByID(webhooks, testWebhookOne.ID) + require.Equal(t, "test-name", webhookOneResponse.Name) + require.Equal(t, workspaceID, *webhookOneResponse.WorkspaceID) + require.Equal(t, WebhookModeSpecific, testWebhookOne.Mode) }) t.Run("webhook creation with multiple triggers should work", func(t *testing.T) { @@ -124,6 +134,7 @@ func TestWebhookScanLogs(t *testing.T) { Triggers: Triggers{ {TriggerType: TriggerTypeTaskLog, Condition: map[string]any{"regex": r0}}, }, + Mode: WebhookModeWorkspace, } w1 := &Webhook{ WebhookType: WebhookTypeDefault, @@ -132,6 +143,7 @@ func TestWebhookScanLogs(t *testing.T) { {TriggerType: TriggerTypeTaskLog, Condition: map[string]any{"regex": r0}}, {TriggerType: TriggerTypeTaskLog, Condition: map[string]any{"regex": r1}}, }, + Mode: WebhookModeWorkspace, } w2 := &Webhook{ WebhookType: WebhookTypeDefault, @@ -139,6 +151,7 @@ func TestWebhookScanLogs(t *testing.T) { Triggers: Triggers{ {TriggerType: TriggerTypeTaskLog, Condition: map[string]any{"regex": r2}}, }, + Mode: WebhookModeWorkspace, } require.NoError(t, manager.addWebhook(ctx, w0)) @@ -388,26 +401,31 @@ var ( ID: 1000, URL: "http://testwebhook.com", WebhookType: WebhookTypeSlack, + Mode: WebhookModeWorkspace, } testWebhookTwo = Webhook{ ID: 2000, URL: "http://testwebhooktwo.com", WebhookType: WebhookTypeDefault, + Mode: WebhookModeWorkspace, } testWebhookThree = Webhook{ ID: 3000, URL: "http://testwebhookthree.com", WebhookType: WebhookTypeSlack, + Mode: WebhookModeWorkspace, } testWebhookFour = Webhook{ ID: 6000, URL: "http://twebhook.com", WebhookType: WebhookTypeSlack, + Mode: WebhookModeWorkspace, } testWebhookFive = Webhook{ ID: 7000, URL: "http://twebhooktwo.com", WebhookType: WebhookTypeDefault, + Mode: WebhookModeWorkspace, } testWebhookFourTrigger = Trigger{ ID: 6001, @@ -484,6 +502,7 @@ func mockWebhook() *Webhook { return &Webhook{ URL: uuid.New().String(), WebhookType: WebhookTypeDefault, + Mode: WebhookModeWorkspace, } } @@ -511,6 +530,7 @@ func TestDequeueEvents(t *testing.T) { }, }, WebhookType: WebhookTypeDefault, + Mode: WebhookModeWorkspace, })) t.Run("dequeueing and consuming a event should work", func(t *testing.T) { diff --git a/master/internal/webhooks/shipper_test.go b/master/internal/webhooks/shipper_test.go index d91e43226de..8431f3408da 100644 --- a/master/internal/webhooks/shipper_test.go +++ b/master/internal/webhooks/shipper_test.go @@ -99,6 +99,7 @@ func TestShipper(t *testing.T) { }, }, WebhookType: WebhookTypeDefault, + Mode: WebhookModeWorkspace, })) // And one that just fires once. require.NoError(t, AddWebhook(ctx, &Webhook{ @@ -118,6 +119,7 @@ func TestShipper(t *testing.T) { }, }, WebhookType: WebhookTypeDefault, + Mode: WebhookModeWorkspace, })) t.Log("build shipper") diff --git a/master/internal/webhooks/webhook.go b/master/internal/webhooks/webhook.go index 0b75f939c27..a8b716efb33 100644 --- a/master/internal/webhooks/webhook.go +++ b/master/internal/webhooks/webhook.go @@ -32,26 +32,43 @@ type Webhook struct { ID WebhookID `bun:"id,pk,autoincrement"` WebhookType WebhookType `bun:"webhook_type,notnull"` URL string `bun:"url,notnull"` + Mode WebhookMode `bun:"mode,notnull"` + WorkspaceID *int32 `bun:"workspace_id"` + Name string `bun:"name,notnull"` Triggers Triggers `bun:"rel:has-many,join:id=webhook_id"` } // WebhookFromProto returns a model Webhook from a proto definition. func WebhookFromProto(w *webhookv1.Webhook) Webhook { + var workspaceID *int32 + if w.WorkspaceId != 0 { + workspaceID = &(w.WorkspaceId) + } return Webhook{ URL: w.Url, Triggers: TriggersFromProto(w.Triggers), WebhookType: WebhookTypeFromProto(w.WebhookType), + Name: w.Name, + WorkspaceID: workspaceID, + Mode: WebhookModeFromProto(w.Mode), } } // Proto converts a webhook to its protobuf representation. func (w *Webhook) Proto() *webhookv1.Webhook { + var workspaceID int32 + if w.WorkspaceID != nil { + workspaceID = *(w.WorkspaceID) + } return &webhookv1.Webhook{ Id: int32(w.ID), Url: w.URL, Triggers: w.Triggers.Proto(), WebhookType: w.WebhookType.Proto(), + Name: w.Name, + Mode: w.Mode.Proto(), + WorkspaceId: workspaceID, } } @@ -61,6 +78,9 @@ type WebhookID int // WebhookType is type for the WebhookType enum. type WebhookType string +// WebhookMode is mode for the WebhookMode enum. +type WebhookMode string + // Triggers is a slice of Trigger objects—primarily useful for its methods. type Triggers []*Trigger @@ -145,6 +165,24 @@ const ( WebhookTypeSlack WebhookType = "SLACK" ) +const ( + // WebhookModeWorkspace represents the webhook will be triggered by all experiment in the workspace. + WebhookModeWorkspace WebhookMode = "WORKSPACE" + // WebhookModeSpecific represents the webhook will only be triggered by experiment with + // matching configuration in the same workspace as the web hook. + WebhookModeSpecific WebhookMode = "SPECIFIC" +) + +// WebhookModeFromProto returns a WebhookMode from a proto. +func WebhookModeFromProto(w webhookv1.WebhookMode) WebhookMode { + switch w { + case webhookv1.WebhookMode_WEBHOOK_MODE_SPECIFIC: + return WebhookModeSpecific + default: + return WebhookModeWorkspace + } +} + // WebhookTypeFromProto returns a WebhookType from a proto. func WebhookTypeFromProto(w webhookv1.WebhookType) WebhookType { switch w { @@ -185,6 +223,16 @@ func (w WebhookType) Proto() webhookv1.WebhookType { } } +// Proto returns a proto from a WebhookMode. +func (w WebhookMode) Proto() webhookv1.WebhookMode { + switch w { + case WebhookModeSpecific: + return webhookv1.WebhookMode_WEBHOOK_MODE_SPECIFIC + default: + return webhookv1.WebhookMode_WEBHOOK_MODE_WORKSPACE + } +} + // Proto returns a proto from a TriggerType. func (t TriggerType) Proto() webhookv1.TriggerType { switch t { diff --git a/master/static/migrations/20240820114209_extend-webhooks-table.tx.up.sql b/master/static/migrations/20240820114209_extend-webhooks-table.tx.up.sql new file mode 100644 index 00000000000..16ee8fe7f34 --- /dev/null +++ b/master/static/migrations/20240820114209_extend-webhooks-table.tx.up.sql @@ -0,0 +1,11 @@ +CREATE TYPE public.webhook_mode AS ENUM ( + 'WORKSPACE', + 'SPECIFIC' +); + +ALTER table public.webhooks + ADD COLUMN workspace_id INT REFERENCES workspaces(id) DEFAULT NULL, + ADD COLUMN name text NOT NULL DEFAULT md5(random()::text), + ADD COLUMN mode public.webhook_mode NOT NULL DEFAULT 'WORKSPACE'; + +CREATE UNIQUE INDEX name_workspace_key on public.webhooks (name, workspace_id); diff --git a/proto/pkg/webhookv1/webhook.pb.go b/proto/pkg/webhookv1/webhook.pb.go index fa7c42a811e..d5634a1ebca 100644 --- a/proto/pkg/webhookv1/webhook.pb.go +++ b/proto/pkg/webhookv1/webhook.pb.go @@ -73,6 +73,60 @@ func (WebhookType) EnumDescriptor() ([]byte, []int) { return file_determined_webhook_v1_webhook_proto_rawDescGZIP(), []int{0} } +// Enum values for webhook mode. +type WebhookMode int32 + +const ( + // Default value + WebhookMode_WEBHOOK_MODE_UNSPECIFIED WebhookMode = 0 + // Webhook will be triggered by all experiment in the workspace + WebhookMode_WEBHOOK_MODE_WORKSPACE WebhookMode = 1 + // Webhook will only be triggered by experiment with matching configuration in + // the same workspace as the web hook + WebhookMode_WEBHOOK_MODE_SPECIFIC WebhookMode = 2 +) + +// Enum value maps for WebhookMode. +var ( + WebhookMode_name = map[int32]string{ + 0: "WEBHOOK_MODE_UNSPECIFIED", + 1: "WEBHOOK_MODE_WORKSPACE", + 2: "WEBHOOK_MODE_SPECIFIC", + } + WebhookMode_value = map[string]int32{ + "WEBHOOK_MODE_UNSPECIFIED": 0, + "WEBHOOK_MODE_WORKSPACE": 1, + "WEBHOOK_MODE_SPECIFIC": 2, + } +) + +func (x WebhookMode) Enum() *WebhookMode { + p := new(WebhookMode) + *p = x + return p +} + +func (x WebhookMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WebhookMode) Descriptor() protoreflect.EnumDescriptor { + return file_determined_webhook_v1_webhook_proto_enumTypes[1].Descriptor() +} + +func (WebhookMode) Type() protoreflect.EnumType { + return &file_determined_webhook_v1_webhook_proto_enumTypes[1] +} + +func (x WebhookMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WebhookMode.Descriptor instead. +func (WebhookMode) EnumDescriptor() ([]byte, []int) { + return file_determined_webhook_v1_webhook_proto_rawDescGZIP(), []int{1} +} + // Enum values for expected trigger types. type TriggerType int32 @@ -114,11 +168,11 @@ func (x TriggerType) String() string { } func (TriggerType) Descriptor() protoreflect.EnumDescriptor { - return file_determined_webhook_v1_webhook_proto_enumTypes[1].Descriptor() + return file_determined_webhook_v1_webhook_proto_enumTypes[2].Descriptor() } func (TriggerType) Type() protoreflect.EnumType { - return &file_determined_webhook_v1_webhook_proto_enumTypes[1] + return &file_determined_webhook_v1_webhook_proto_enumTypes[2] } func (x TriggerType) Number() protoreflect.EnumNumber { @@ -127,7 +181,7 @@ func (x TriggerType) Number() protoreflect.EnumNumber { // Deprecated: Use TriggerType.Descriptor instead. func (TriggerType) EnumDescriptor() ([]byte, []int) { - return file_determined_webhook_v1_webhook_proto_rawDescGZIP(), []int{1} + return file_determined_webhook_v1_webhook_proto_rawDescGZIP(), []int{2} } // Representation of a Webhook @@ -144,6 +198,12 @@ type Webhook struct { Triggers []*Trigger `protobuf:"bytes,3,rep,name=triggers,proto3" json:"triggers,omitempty"` // The type of the webhook. WebhookType WebhookType `protobuf:"varint,4,opt,name=webhook_type,json=webhookType,proto3,enum=determined.webhook.v1.WebhookType" json:"webhook_type,omitempty"` + // The name of the webhook. + Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"` + // The workspace of the webhook. + WorkspaceId int32 `protobuf:"varint,6,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"` + // The mode of the webhook. + Mode WebhookMode `protobuf:"varint,7,opt,name=mode,proto3,enum=determined.webhook.v1.WebhookMode" json:"mode,omitempty"` } func (x *Webhook) Reset() { @@ -206,6 +266,27 @@ func (x *Webhook) GetWebhookType() WebhookType { return WebhookType_WEBHOOK_TYPE_UNSPECIFIED } +func (x *Webhook) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Webhook) GetWorkspaceId() int32 { + if x != nil { + return x.WorkspaceId + } + return 0 +} + +func (x *Webhook) GetMode() WebhookMode { + if x != nil { + return x.Mode + } + return WebhookMode_WEBHOOK_MODE_UNSPECIFIED +} + // Representation for a Trigger for a Webhook type Trigger struct { state protoimpl.MessageState @@ -294,7 +375,7 @@ var file_determined_webhook_v1_webhook_proto_rawDesc = []byte{ 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, - 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xca, 0x01, 0x0a, 0x07, 0x57, 0x65, 0x62, + 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc7, 0x02, 0x0a, 0x07, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x3a, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, @@ -305,42 +386,56 @@ var file_determined_webhook_v1_webhook_proto_rawDesc = []byte{ 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x77, 0x65, - 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x1a, 0x92, 0x41, 0x17, 0x0a, 0x15, - 0xd2, 0x01, 0x03, 0x75, 0x72, 0x6c, 0xd2, 0x01, 0x0c, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd5, 0x01, 0x0a, 0x07, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, - 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x45, 0x0a, 0x0c, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x2e, 0x76, 0x31, 0x2e, - 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x72, 0x69, - 0x67, 0x67, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, - 0x72, 0x75, 0x63, 0x74, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1d, 0x0a, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x09, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x49, 0x64, 0x3a, 0x1d, - 0x92, 0x41, 0x1a, 0x0a, 0x18, 0xd2, 0x01, 0x15, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x5d, 0x0a, - 0x0b, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, - 0x57, 0x45, 0x42, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, - 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x57, 0x45, - 0x42, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, - 0x4c, 0x54, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x45, 0x42, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x4c, 0x41, 0x43, 0x4b, 0x10, 0x02, 0x2a, 0x9c, 0x01, 0x0a, - 0x0b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, - 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, - 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x54, 0x52, - 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x52, - 0x49, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x48, 0x41, 0x4e, - 0x47, 0x45, 0x10, 0x01, 0x12, 0x2a, 0x0a, 0x26, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x48, 0x52, 0x45, - 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, 0x02, - 0x12, 0x19, 0x0a, 0x15, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, - 0x5f, 0x54, 0x41, 0x53, 0x4b, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x03, 0x42, 0x39, 0x5a, 0x37, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x65, 0x64, 0x2d, 0x61, 0x69, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x77, 0x65, 0x62, - 0x68, 0x6f, 0x6f, 0x6b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, + 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x36, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, + 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2e, 0x77, 0x65, 0x62, 0x68, + 0x6f, 0x6f, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x4d, 0x6f, + 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x3a, 0x28, 0x92, 0x41, 0x25, 0x0a, 0x23, 0xd2, + 0x01, 0x03, 0x75, 0x72, 0x6c, 0xd2, 0x01, 0x0c, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0xd2, 0x01, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0xd2, 0x01, 0x04, 0x6d, 0x6f, + 0x64, 0x65, 0x22, 0xd5, 0x01, 0x0a, 0x07, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x45, + 0x0a, 0x0c, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, + 0x64, 0x2e, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x69, + 0x67, 0x67, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0b, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, + 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x35, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, + 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x09, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x49, 0x64, 0x3a, 0x1d, 0x92, 0x41, 0x1a, + 0x0a, 0x18, 0xd2, 0x01, 0x15, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x5d, 0x0a, 0x0b, 0x57, 0x65, + 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x57, 0x45, 0x42, + 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x57, 0x45, 0x42, 0x48, 0x4f, + 0x4f, 0x4b, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, + 0x01, 0x12, 0x16, 0x0a, 0x12, 0x57, 0x45, 0x42, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x53, 0x4c, 0x41, 0x43, 0x4b, 0x10, 0x02, 0x2a, 0x62, 0x0a, 0x0b, 0x57, 0x65, 0x62, + 0x68, 0x6f, 0x6f, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1c, 0x0a, 0x18, 0x57, 0x45, 0x42, 0x48, + 0x4f, 0x4f, 0x4b, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x57, 0x45, 0x42, 0x48, 0x4f, 0x4f, + 0x4b, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, + 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x57, 0x45, 0x42, 0x48, 0x4f, 0x4f, 0x4b, 0x5f, 0x4d, 0x4f, + 0x44, 0x45, 0x5f, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x9c, 0x01, + 0x0a, 0x0b, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, + 0x18, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x54, + 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x45, + 0x52, 0x49, 0x4d, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x48, 0x41, + 0x4e, 0x47, 0x45, 0x10, 0x01, 0x12, 0x2a, 0x0a, 0x26, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x48, 0x52, + 0x45, 0x53, 0x48, 0x4f, 0x4c, 0x44, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x45, 0x44, 0x10, + 0x02, 0x12, 0x19, 0x0a, 0x15, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x54, 0x41, 0x53, 0x4b, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x03, 0x42, 0x39, 0x5a, 0x37, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x65, 0x64, 0x2d, 0x61, 0x69, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6e, 0x65, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x77, 0x65, + 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -355,25 +450,27 @@ func file_determined_webhook_v1_webhook_proto_rawDescGZIP() []byte { return file_determined_webhook_v1_webhook_proto_rawDescData } -var file_determined_webhook_v1_webhook_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_determined_webhook_v1_webhook_proto_enumTypes = make([]protoimpl.EnumInfo, 3) var file_determined_webhook_v1_webhook_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_determined_webhook_v1_webhook_proto_goTypes = []interface{}{ (WebhookType)(0), // 0: determined.webhook.v1.WebhookType - (TriggerType)(0), // 1: determined.webhook.v1.TriggerType - (*Webhook)(nil), // 2: determined.webhook.v1.Webhook - (*Trigger)(nil), // 3: determined.webhook.v1.Trigger - (*_struct.Struct)(nil), // 4: google.protobuf.Struct + (WebhookMode)(0), // 1: determined.webhook.v1.WebhookMode + (TriggerType)(0), // 2: determined.webhook.v1.TriggerType + (*Webhook)(nil), // 3: determined.webhook.v1.Webhook + (*Trigger)(nil), // 4: determined.webhook.v1.Trigger + (*_struct.Struct)(nil), // 5: google.protobuf.Struct } var file_determined_webhook_v1_webhook_proto_depIdxs = []int32{ - 3, // 0: determined.webhook.v1.Webhook.triggers:type_name -> determined.webhook.v1.Trigger + 4, // 0: determined.webhook.v1.Webhook.triggers:type_name -> determined.webhook.v1.Trigger 0, // 1: determined.webhook.v1.Webhook.webhook_type:type_name -> determined.webhook.v1.WebhookType - 1, // 2: determined.webhook.v1.Trigger.trigger_type:type_name -> determined.webhook.v1.TriggerType - 4, // 3: determined.webhook.v1.Trigger.condition:type_name -> google.protobuf.Struct - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 1, // 2: determined.webhook.v1.Webhook.mode:type_name -> determined.webhook.v1.WebhookMode + 2, // 3: determined.webhook.v1.Trigger.trigger_type:type_name -> determined.webhook.v1.TriggerType + 5, // 4: determined.webhook.v1.Trigger.condition:type_name -> google.protobuf.Struct + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] 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 } func init() { file_determined_webhook_v1_webhook_proto_init() } @@ -412,7 +509,7 @@ func file_determined_webhook_v1_webhook_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_determined_webhook_v1_webhook_proto_rawDesc, - NumEnums: 2, + NumEnums: 3, NumMessages: 2, NumExtensions: 0, NumServices: 0, diff --git a/proto/src/determined/webhook/v1/webhook.proto b/proto/src/determined/webhook/v1/webhook.proto index 3ccac55f541..ec81a2d275a 100644 --- a/proto/src/determined/webhook/v1/webhook.proto +++ b/proto/src/determined/webhook/v1/webhook.proto @@ -15,6 +15,17 @@ enum WebhookType { WEBHOOK_TYPE_SLACK = 2; } +// Enum values for webhook mode. +enum WebhookMode { + // Default value + WEBHOOK_MODE_UNSPECIFIED = 0; + // Webhook will be triggered by all experiment in the workspace + WEBHOOK_MODE_WORKSPACE = 1; + // Webhook will only be triggered by experiment with matching configuration in + // the same workspace as the web hook + WEBHOOK_MODE_SPECIFIC = 2; +} + // Enum values for expected trigger types. enum TriggerType { // Default value @@ -30,7 +41,7 @@ enum TriggerType { // Representation of a Webhook message Webhook { option (grpc.gateway.protoc_gen_swagger.options.openapiv2_schema) = { - json_schema: { required: [ "url", "webhook_type" ] } + json_schema: { required: [ "url", "webhook_type", "name", "mode" ] } }; // The id of the webhook. int32 id = 1; @@ -40,6 +51,12 @@ message Webhook { repeated Trigger triggers = 3; // The type of the webhook. WebhookType webhook_type = 4; + // The name of the webhook. + string name = 5; + // The workspace of the webhook. + int32 workspace_id = 6; + // The mode of the webhook. + WebhookMode mode = 7; } // Representation for a Trigger for a Webhook diff --git a/webui/react/src/components/Table/Table.tsx b/webui/react/src/components/Table/Table.tsx index d5e40113bce..2ad88eaeff9 100644 --- a/webui/react/src/components/Table/Table.tsx +++ b/webui/react/src/components/Table/Table.tsx @@ -182,7 +182,7 @@ export const taskWorkspaceRenderer = ( const workspace = workspaces.find((u) => u.id === record.workspaceId); const workspaceId = record.workspaceId; const isUncategorized = workspaceId === 1; - + if (!workspace) return null; return (