From a8094115d70b27213a101dece611a33e7f77316a Mon Sep 17 00:00:00 2001 From: gt2345 Date: Wed, 14 Aug 2024 11:05:40 -0500 Subject: [PATCH 01/10] feat: Add workspaceId, mode, name to webhook --- harness/determined/common/api/bindings.py | 24 +++ master/Makefile | 2 +- .../webhooks/postgres_webhook_intg_test.go | 19 ++ master/internal/webhooks/shipper_test.go | 2 + master/internal/webhooks/webhook.go | 45 ++++ ...0813125721_extend-webhooks-table.tx.up.sql | 11 + proto/pkg/webhookv1/webhook.pb.go | 199 +++++++++++++----- proto/src/determined/webhook/v1/webhook.proto | 16 +- webui/react/src/components/Table/Table.tsx | 2 +- .../src/components/WebhookCreateModal.tsx | 64 +++++- webui/react/src/hooks/useFeature.ts | 8 +- webui/react/src/pages/WebhookList.settings.ts | 37 +++- webui/react/src/pages/WebhookList.tsx | 105 ++++++++- webui/react/src/services/api-ts-sdk/api.ts | 28 +++ webui/react/src/services/decoder.ts | 6 + webui/react/src/types.ts | 3 + 16 files changed, 499 insertions(+), 72 deletions(-) create mode 100644 master/static/migrations/20240813125721_extend-webhooks-table.tx.up.sql diff --git a/harness/determined/common/api/bindings.py b/harness/determined/common/api/bindings.py index c468b0a6dab..e547fb7d4c4 100644 --- a/harness/determined/common/api/bindings.py +++ b/harness/determined/common/api/bindings.py @@ -16899,25 +16899,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"]), } @@ -16925,10 +16935,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, } @@ -16936,8 +16950,18 @@ 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_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 + """ + 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/Makefile b/master/Makefile index a326e51fcce..cb7f83803cc 100644 --- a/master/Makefile +++ b/master/Makefile @@ -175,7 +175,7 @@ test-intg: export DET_INTEGRATION_POSTGRES_URL ?= postgres://postgres:postgres@l test-intg: export DET_INTEGRATION_ES_HOST ?= localhost test-intg: export DET_INTEGRATION_ES_PORT ?= 9200 test-intg: build/mock_gen.stamp - gotestsum --junitfile test-intg.junit.xml -- -tags=integration -race -coverprofile=coverage.out -covermode atomic -cover ./... + gotestsum --junitfile test-intg.junit.xml -- -tags=integration -race -coverprofile=coverage.out -covermode atomic -cover ./internal/webhooks .PHONY: pre-package pre-package: diff --git a/master/internal/webhooks/postgres_webhook_intg_test.go b/master/internal/webhooks/postgres_webhook_intg_test.go index 871eeb06395..b8b5f22f0b1 100644 --- a/master/internal/webhooks/postgres_webhook_intg_test.go +++ b/master/internal/webhooks/postgres_webhook_intg_test.go @@ -78,9 +78,18 @@ 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) + webhookOneResponse := getWebhookByID(webhooks, testWebhookOne.ID) + require.Equal(t, webhookOneResponse.Name, "test-name") + require.Equal(t, *webhookOneResponse.WorkspaceID, workspaceID) + require.Equal(t, testWebhookOne.Mode, WebhookModeSpecific) }) t.Run("webhook creation with multiple triggers should work", func(t *testing.T) { @@ -124,6 +133,7 @@ func TestWebhookScanLogs(t *testing.T) { Triggers: Triggers{ {TriggerType: TriggerTypeTaskLog, Condition: map[string]any{"regex": r0}}, }, + Mode: WebhookModeWorkspace, } w1 := &Webhook{ WebhookType: WebhookTypeDefault, @@ -132,6 +142,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 +150,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 +400,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 +501,7 @@ func mockWebhook() *Webhook { return &Webhook{ URL: uuid.New().String(), WebhookType: WebhookTypeDefault, + Mode: WebhookModeWorkspace, } } @@ -511,6 +529,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..91ca34fabe0 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 +// WebhookType is type 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,22 @@ const ( WebhookTypeSlack WebhookType = "SLACK" ) +const ( + // WebhookTypeDefault 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" +) + +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 +221,15 @@ func (w WebhookType) Proto() webhookv1.WebhookType { } } +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/20240813125721_extend-webhooks-table.tx.up.sql b/master/static/migrations/20240813125721_extend-webhooks-table.tx.up.sql new file mode 100644 index 00000000000..16ee8fe7f34 --- /dev/null +++ b/master/static/migrations/20240813125721_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..6c42c5fd85e 100644 --- a/proto/pkg/webhookv1/webhook.pb.go +++ b/proto/pkg/webhookv1/webhook.pb.go @@ -73,6 +73,55 @@ func (WebhookType) EnumDescriptor() ([]byte, []int) { return file_determined_webhook_v1_webhook_proto_rawDescGZIP(), []int{0} } +// Enum values for webhook mode. +type WebhookMode int32 + +const ( + // Webhook will be triggered by all experiment in the workspace + WebhookMode_WEBHOOK_MODE_WORKSPACE WebhookMode = 0 + // Webhook will only be triggered by experiment with matching configuration in the same workspace as the web hook + WebhookMode_WEBHOOK_MODE_SPECIFIC WebhookMode = 1 +) + +// Enum value maps for WebhookMode. +var ( + WebhookMode_name = map[int32]string{ + 0: "WEBHOOK_MODE_WORKSPACE", + 1: "WEBHOOK_MODE_SPECIFIC", + } + WebhookMode_value = map[string]int32{ + "WEBHOOK_MODE_WORKSPACE": 0, + "WEBHOOK_MODE_SPECIFIC": 1, + } +) + +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 +163,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 +176,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 +193,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 +261,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_WORKSPACE +} + // Representation for a Trigger for a Webhook type Trigger struct { state protoimpl.MessageState @@ -294,7 +370,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 +381,55 @@ 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, 0x44, 0x0a, 0x0b, 0x57, 0x65, 0x62, + 0x68, 0x6f, 0x6f, 0x6b, 0x4d, 0x6f, 0x64, 0x65, 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, 0x00, 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, 0x01, 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 +444,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 +503,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..576e88da703 100644 --- a/proto/src/determined/webhook/v1/webhook.proto +++ b/proto/src/determined/webhook/v1/webhook.proto @@ -15,6 +15,14 @@ enum WebhookType { WEBHOOK_TYPE_SLACK = 2; } +// Enum values for webhook mode. +enum WebhookMode { + // Webhook will be triggered by all experiment in the workspace + WEBHOOK_MODE_WORKSPACE = 0; + // Webhook will only be triggered by experiment with matching configuration in the same workspace as the web hook + WEBHOOK_MODE_SPECIFIC = 1; +} + // Enum values for expected trigger types. enum TriggerType { // Default value @@ -30,7 +38,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 +48,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 (
void; } @@ -24,6 +27,9 @@ interface FormInputs { triggerEvents: (typeof triggerEvents)[number][]; url: string; webhookType: V1WebhookType; + name: string; + workspaceId: number; + mode: V1WebhookMode; } const typeOptions = [ @@ -50,11 +56,26 @@ const triggerOptions = [ value: V1TriggerType.TASKLOG, }, ]; +const modeOptions = [ + { + label: 'All experiments in Workspace', + value: V1WebhookMode.WORKSPACE, + }, + { + label: 'Specific experiment with matching configuration', + value: V1WebhookMode.SPECIFIC, + }, +]; const WebhookCreateModalComponent: React.FC = ({ onSuccess }: Props) => { const idPrefix = useId(); const [form] = Form.useForm(); const [disabled, setDisabled] = useState(true); const triggers = Form.useWatch('triggerEvents', form); + const f_webhook = useFeature().isOn('webhook_improvement'); + const workspaces = Loadable.match(useObservable(workspaceStore.workspaces), { + _: () => [], + Loaded: (ws) => ws, + }); const onChange = useCallback(() => { const fields = form.getFieldsError(); @@ -74,6 +95,8 @@ const WebhookCreateModalComponent: React.FC = ({ onSuccess }: Props) => { try { if (values) { await createWebhook({ + mode: values.mode, + name: values.name, triggers: values.triggerEvents.map((state) => { if (state === 'TRIGGER_TYPE_TASK_LOG') { return { @@ -88,6 +111,7 @@ const WebhookCreateModalComponent: React.FC = ({ onSuccess }: Props) => { }), url: values.url, webhookType: values.webhookType, + workspaceId: values.workspaceId, }); onSuccess?.(); routeToReactUrl(paths.webhooks()); @@ -111,13 +135,14 @@ const WebhookCreateModalComponent: React.FC = ({ onSuccess }: Props) => { type: ErrorType.Server, }); } + throw e; } }, [form, onSuccess]); return ( = ({ onSuccess }: Props) => { id={idPrefix + FORM_ID} layout="vertical" onFieldsChange={onChange}> + {f_webhook && ( + <> + + + + + + + + )} = ({ onSuccess }: Props) => { ]}> + {f_webhook && ( + + {workspaces.map((workspace: Workspace) => ( )} = { }, webhook_improvement: { defaultValue: false, - description: 'Webhook improvement', - friendlyName: 'Webhook improvement', + description: 'Allow webhooks to monitor experiments by workspace', + friendlyName: 'Webhook Improvement', }, } as const; export const FEATURE_SETTINGS_PATH = 'global-features'; diff --git a/webui/react/src/pages/WebhookList.settings.ts b/webui/react/src/pages/WebhookList.settings.ts index c150378026c..74598c07551 100644 --- a/webui/react/src/pages/WebhookList.settings.ts +++ b/webui/react/src/pages/WebhookList.settings.ts @@ -10,12 +10,12 @@ export type WebhookColumnName = | 'url' | 'webhookType' | 'name' - | 'workspace' + | 'workspaceIds' | 'mode'; export const DEFAULT_COLUMNS: WebhookColumnName[] = [ 'name', - 'workspace', + 'workspaceIds', 'url', 'webhookType', 'mode', @@ -29,12 +29,12 @@ export const DEFAULT_COLUMN_WIDTHS: Record = { triggers: 150, url: 150, webhookType: 60, - workspace: 80, + workspaceIds: 80, }; export interface Settings extends InteractiveTableSettings { columns: WebhookColumnName[]; - workspace?: number[]; + workspaceIds?: number[]; } const config: SettingsConfig = { @@ -49,7 +49,7 @@ const config: SettingsConfig = { literal('triggers'), literal('url'), literal('webhookType'), - literal('workspace'), + literal('workspaceIds'), literal('name'), literal('mode'), ]), @@ -81,9 +81,9 @@ const config: SettingsConfig = { storageKey: 'tableOffset', type: number, }, - workspace: { - defaultValue: [], - storageKey: 'workspace', + workspaceIds: { + defaultValue: undefined, + storageKey: 'workspaceIds', type: union([undefinedType, array(number)]), }, }, diff --git a/webui/react/src/pages/WebhookList.tsx b/webui/react/src/pages/WebhookList.tsx index a665c727ec3..13d3047e761 100644 --- a/webui/react/src/pages/WebhookList.tsx +++ b/webui/react/src/pages/WebhookList.tsx @@ -10,7 +10,6 @@ import Icon from 'hew/Icon'; import Message from 'hew/Message'; import { useModal } from 'hew/Modal'; import Row from 'hew/Row'; -import { Loadable } from 'hew/utils/loadable'; import _ from 'lodash'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; @@ -65,10 +64,7 @@ const WebhooksView: React.FC = () => { const { canEditWebhooks } = usePermissions(); - const workspaces = Loadable.match(useObservable(workspaceStore.workspaces), { - _: () => [], - Loaded: (ws) => ws, - }); + const workspaces = useObservable(workspaceStore.workspaces).getOrElse([]); const WebhookCreateModal = useModal(WebhookCreateModalComponent); const WebhookDeleteModal = useModal(WebhookDeleteModalComponent); @@ -104,12 +100,13 @@ const WebhooksView: React.FC = () => { }, [fetchWebhooks]); useEffect(() => { - if (settings.workspace?.length) { - setFilteredWebhooks(webhooks.filter((w) => settings.workspace?.includes(w.workspaceId))); + if (Array.isArray(settings.workspaceIds)) { + const idSet = new Set(settings.workspaceIds); + setFilteredWebhooks(webhooks.filter((w) => idSet.has(w.workspaceId))); } else { setFilteredWebhooks(webhooks); } - }, [webhooks, settings.workspace]); + }, [webhooks, settings.workspaceIds]); const handleDropdown = useCallback( async (key: string, record: Webhook) => { @@ -137,7 +134,7 @@ const WebhooksView: React.FC = () => { (workspaces: string[]) => { updateSettings({ tableOffset: 0, - workspace: + workspaceIds: workspaces.length !== 0 ? workspaces.map((workspace) => Number(workspace)) : undefined, }); }, @@ -145,7 +142,7 @@ const WebhooksView: React.FC = () => { ); const handleWorkspaceFilterReset = useCallback(() => { - updateSettings({ tableOffset: 0, workspace: undefined }); + updateSettings({ tableOffset: 0, workspaceIds: undefined }); }, [updateSettings]); const workspaceFilterDropdown = useCallback( @@ -153,13 +150,13 @@ const WebhooksView: React.FC = () => { ws.toString())} + values={settings.workspaceIds?.map((ws) => ws.toString())} width={220} onFilter={handleWorkspaceFilterApply} onReset={handleWorkspaceFilterReset} /> ), - [handleWorkspaceFilterApply, handleWorkspaceFilterReset, settings.workspace], + [handleWorkspaceFilterApply, handleWorkspaceFilterReset, settings.workspaceIds], ); const columns = useMemo(() => { @@ -198,14 +195,14 @@ const WebhooksView: React.FC = () => { }, { align: 'center', - dataIndex: 'workspace', - defaultWidth: DEFAULT_COLUMN_WIDTHS['workspace'], + dataIndex: 'workspaceIds', + defaultWidth: DEFAULT_COLUMN_WIDTHS['workspaceIds'], filterDropdown: workspaceFilterDropdown, filters: workspaces.map((ws) => ({ text: , value: ws.id, })), - isFiltered: (settings: Settings) => !!settings.workspace?.length, + isFiltered: (settings: Settings) => !!settings.workspaceIds?.length, key: 'workspaceId', render: (_v: string, record: Webhook) => taskWorkspaceRenderer(record, workspaces), title: 'Workspace', @@ -253,9 +250,7 @@ const WebhooksView: React.FC = () => { ] as ColumnDef[]; if (!f_webhook) { - columns.shift(); - columns.shift(); - columns.shift(); + return columns.slice(3); } return columns; From 9f187f8cd9e7ea7f968d297c09de94a306822b94 Mon Sep 17 00:00:00 2001 From: gt2345 Date: Mon, 19 Aug 2024 09:59:23 -0500 Subject: [PATCH 09/10] reset makeFile --- master/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/master/Makefile b/master/Makefile index cb7f83803cc..a326e51fcce 100644 --- a/master/Makefile +++ b/master/Makefile @@ -175,7 +175,7 @@ test-intg: export DET_INTEGRATION_POSTGRES_URL ?= postgres://postgres:postgres@l test-intg: export DET_INTEGRATION_ES_HOST ?= localhost test-intg: export DET_INTEGRATION_ES_PORT ?= 9200 test-intg: build/mock_gen.stamp - gotestsum --junitfile test-intg.junit.xml -- -tags=integration -race -coverprofile=coverage.out -covermode atomic -cover ./internal/webhooks + gotestsum --junitfile test-intg.junit.xml -- -tags=integration -race -coverprofile=coverage.out -covermode atomic -cover ./... .PHONY: pre-package pre-package: From e4011fbed3cb83ec0400123d790b4fefc1d35145 Mon Sep 17 00:00:00 2001 From: gt2345 Date: Tue, 20 Aug 2024 11:43:07 -0500 Subject: [PATCH 10/10] move migration --- ...e.tx.up.sql => 20240820114209_extend-webhooks-table.tx.up.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename master/static/migrations/{20240813125721_extend-webhooks-table.tx.up.sql => 20240820114209_extend-webhooks-table.tx.up.sql} (100%) diff --git a/master/static/migrations/20240813125721_extend-webhooks-table.tx.up.sql b/master/static/migrations/20240820114209_extend-webhooks-table.tx.up.sql similarity index 100% rename from master/static/migrations/20240813125721_extend-webhooks-table.tx.up.sql rename to master/static/migrations/20240820114209_extend-webhooks-table.tx.up.sql