diff --git a/app/api/controller/pullreq/label_assign.go b/app/api/controller/pullreq/label_assign.go index 5019acd61..bff10dd37 100644 --- a/app/api/controller/pullreq/label_assign.go +++ b/app/api/controller/pullreq/label_assign.go @@ -19,6 +19,7 @@ import ( "fmt" "github.com/harness/gitness/app/auth" + events "github.com/harness/gitness/app/events/pullreq" "github.com/harness/gitness/app/services/label" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" @@ -69,6 +70,24 @@ func (c *Controller) AssignLabel( log.Ctx(ctx).Err(err).Msgf("failed to write pull request activity after label unassign") } + // if the label has no value, the newValueID will be nil + var newValueID *int64 + if out.NewLabelValue != nil { + newValueID = &out.NewLabelValue.ID + } + + c.eventReporter.LabelAssigned(ctx, &events.LabelAssignedPayload{ + Base: events.Base{ + PullReqID: pullreq.ID, + SourceRepoID: pullreq.SourceRepoID, + TargetRepoID: pullreq.TargetRepoID, + PrincipalID: session.Principal.ID, + Number: pullreq.Number, + }, + LabelID: out.Label.ID, + ValueID: newValueID, + }) + return out.PullReqLabel, nil } diff --git a/app/events/pullreq/events_label_assigned.go b/app/events/pullreq/events_label_assigned.go new file mode 100644 index 000000000..4fc53dc9a --- /dev/null +++ b/app/events/pullreq/events_label_assigned.go @@ -0,0 +1,55 @@ +// Copyright 2023 Harness, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package events + +import ( + "context" + + "github.com/harness/gitness/events" + + "github.com/rs/zerolog/log" +) + +const LabelAssignedEvent events.EventType = "label-assigned" + +type LabelAssignedPayload struct { + Base + LabelID int64 `json:"label_id"` + ValueID *int64 `json:"value_id"` +} + +func (r *Reporter) LabelAssigned( + ctx context.Context, + payload *LabelAssignedPayload, +) { + if payload == nil { + return + } + + eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, LabelAssignedEvent, payload) + if err != nil { + log.Ctx(ctx).Err(err).Msgf("failed to send pull request label assigned event") + return + } + + log.Ctx(ctx).Debug().Msgf("reported pull request label assigned event with id '%s'", eventID) +} + +func (r *Reader) RegisterLabelAssigned( + fn events.HandlerFunc[*LabelAssignedPayload], + opts ...events.HandlerOption, +) error { + return events.ReaderRegisterEvent(r.innerReader, LabelAssignedEvent, fn, opts...) +} diff --git a/app/services/webhook/handler_pullreq.go b/app/services/webhook/handler_pullreq.go index 98312b11a..ca6433d25 100644 --- a/app/services/webhook/handler_pullreq.go +++ b/app/services/webhook/handler_pullreq.go @@ -318,7 +318,11 @@ func (s *Service) handleEventPullReqComment( sourceRepoInfo := repositoryInfoFrom(ctx, sourceRepo, s.urlProvider) activity, err := s.activityStore.Find(ctx, event.Payload.ActivityID) if err != nil { - return nil, fmt.Errorf("failed to get activity by id for acitivity id %d: %w", event.Payload.ActivityID, err) + return nil, fmt.Errorf( + "failed to get activity by id for acitivity id %d: %w", + event.Payload.ActivityID, + err, + ) } commitInfo, err := s.fetchCommitInfoForEvent(ctx, sourceRepo.GitUID, event.Payload.SourceSHA) if err != nil { @@ -361,6 +365,64 @@ func (s *Service) handleEventPullReqComment( }) } +// PullReqLabelAssignedPayload describes the body of the pullreq label assignment trigger. +type PullReqLabelAssignedPayload struct { + BaseSegment + PullReqSegment + PullReqLabelSegment +} + +func (s *Service) handleEventPullReqLabelAssigned( + ctx context.Context, + event *events.Event[*pullreqevents.LabelAssignedPayload], +) error { + return s.triggerForEventWithPullReq( + ctx, + enum.WebhookTriggerPullReqLabelAssigned, + event.ID, event.Payload.PrincipalID, + event.Payload.PullReqID, + func( + principal *types.Principal, + pr *types.PullReq, + targetRepo, + _ *types.Repository, + ) (any, error) { + label, err := s.labelStore.FindByID(ctx, event.Payload.LabelID) + if err != nil { + return nil, fmt.Errorf("failed to find label by id: %w", err) + } + + var labelValueKey *string + if event.Payload.ValueID != nil { + value, err := s.labelStore.FindByID(ctx, *event.Payload.ValueID) + if err != nil { + return nil, fmt.Errorf("failed to find label value by id: %d %w", *event.Payload.ValueID, err) + } + labelValueKey = &value.Key + } + + targetRepoInfo := repositoryInfoFrom(ctx, targetRepo, s.urlProvider) + return &PullReqLabelAssignedPayload{ + BaseSegment: BaseSegment{ + Trigger: enum.WebhookTriggerPullReqCommentCreated, + Repo: targetRepoInfo, + Principal: principalInfoFrom(principal.ToPrincipalInfo()), + }, + PullReqSegment: PullReqSegment{ + PullReq: pullReqInfoFrom(ctx, pr, targetRepo, s.urlProvider), + }, + PullReqLabelSegment: PullReqLabelSegment{ + LabelInfo: LabelInfo{ + ID: event.Payload.LabelID, + Key: label.Key, + ValueID: event.Payload.ValueID, + Value: labelValueKey, + }, + }, + }, nil + }) +} + // PullReqUpdatedPayload describes the body of the pullreq updated trigger. type PullReqUpdatedPayload struct { BaseSegment diff --git a/app/services/webhook/service.go b/app/services/webhook/service.go index 643a61d6a..909e3c056 100644 --- a/app/services/webhook/service.go +++ b/app/services/webhook/service.go @@ -91,6 +91,7 @@ type Service struct { principalStore store.PrincipalStore git git.Interface activityStore store.PullReqActivityStore + labelStore store.LabelStore encrypter encrypt.Encrypter secureHTTPClient *http.Client @@ -118,6 +119,8 @@ func NewService( principalStore store.PrincipalStore, git git.Interface, encrypter encrypt.Encrypter, + labelStore store.LabelStore, + ) (*Service, error) { if err := config.Prepare(); err != nil { return nil, fmt.Errorf("provided webhook service config is invalid: %w", err) @@ -142,6 +145,8 @@ func NewService( insecureHTTPClientInternal: newHTTPClient(config.AllowLoopback, true, true), config: config, + + labelStore: labelStore, } _, err := gitReaderFactory.Launch(ctx, eventsReaderGroupName, config.EventReaderName, @@ -187,6 +192,7 @@ func NewService( _ = r.RegisterCommentCreated(service.handleEventPullReqComment) _ = r.RegisterMerged(service.handleEventPullReqMerged) _ = r.RegisterUpdated(service.handleEventPullReqUpdated) + _ = r.RegisterLabelAssigned(service.handleEventPullReqLabelAssigned) return nil }) diff --git a/app/services/webhook/types.go b/app/services/webhook/types.go index 36c491746..05e09ea31 100644 --- a/app/services/webhook/types.go +++ b/app/services/webhook/types.go @@ -80,6 +80,11 @@ type PullReqCommentSegment struct { CommentInfo CommentInfo `json:"comment"` } +// PullReqLabelSegment contains details for all pull req label related payloads for webhooks. +type PullReqLabelSegment struct { + LabelInfo LabelInfo `json:"label"` +} + // PullReqUpdateSegment contains details what has been updated in the pull request. type PullReqUpdateSegment struct { TitleChanged bool `json:"title_changed"` @@ -293,3 +298,10 @@ type CommentInfo struct { ParentID *int64 `json:"parent_id,omitempty"` Text string `json:"text"` } + +type LabelInfo struct { + ID int64 `json:"id"` + Key string `json:"key"` + ValueID *int64 `json:"value_id,omitempty"` + Value *string `json:"value,omitempty"` +} diff --git a/app/services/webhook/wire.go b/app/services/webhook/wire.go index 6163662c2..d64316466 100644 --- a/app/services/webhook/wire.go +++ b/app/services/webhook/wire.go @@ -50,8 +50,23 @@ func ProvideService( principalStore store.PrincipalStore, git git.Interface, encrypter encrypt.Encrypter, + labelStore store.LabelStore, ) (*Service, error) { - return NewService(ctx, config, tx, gitReaderFactory, prReaderFactory, - webhookStore, webhookExecutionStore, spaceStore, repoStore, pullreqStore, activityStore, - urlProvider, principalStore, git, encrypter) + return NewService( + ctx, + config, + tx, + gitReaderFactory, + prReaderFactory, + webhookStore, + webhookExecutionStore, + spaceStore, repoStore, + pullreqStore, + activityStore, + urlProvider, + principalStore, + git, + encrypter, + labelStore, + ) } diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index edef9e11f..b0ace3f4a 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -365,7 +365,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro webhookConfig := server.ProvideWebhookConfig(config) webhookStore := database.ProvideWebhookStore(db) webhookExecutionStore := database.ProvideWebhookExecutionStore(db) - webhookService, err := webhook.ProvideService(ctx, webhookConfig, transactor, readerFactory, eventsReaderFactory, webhookStore, webhookExecutionStore, spaceStore, repoStore, pullReqStore, pullReqActivityStore, provider, principalStore, gitInterface, encrypter) + webhookService, err := webhook.ProvideService(ctx, webhookConfig, transactor, readerFactory, eventsReaderFactory, webhookStore, webhookExecutionStore, spaceStore, repoStore, pullReqStore, pullReqActivityStore, provider, principalStore, gitInterface, encrypter, labelStore) if err != nil { return nil, err } diff --git a/types/enum/webhook.go b/types/enum/webhook.go index ae66def8a..ef533f331 100644 --- a/types/enum/webhook.go +++ b/types/enum/webhook.go @@ -160,6 +160,8 @@ const ( WebhookTriggerPullReqMerged WebhookTrigger = "pullreq_merged" // WebhookTriggerPullReqUpdated gets triggered when a pull request gets updated. WebhookTriggerPullReqUpdated WebhookTrigger = "pullreq_updated" + // WebhookTriggerPullReqLabelAssigned gets triggered when a label is assigned to a pull request. + WebhookTriggerPullReqLabelAssigned WebhookTrigger = "pullreq_label_assigned" ) var webhookTriggers = sortEnum([]WebhookTrigger{ @@ -176,4 +178,5 @@ var webhookTriggers = sortEnum([]WebhookTrigger{ WebhookTriggerPullReqClosed, WebhookTriggerPullReqCommentCreated, WebhookTriggerPullReqMerged, + WebhookTriggerPullReqLabelAssigned, })