feat: [CODE-2366]: new webhook event for label assignment (#2810)

* feat: [CODE-2336]: lint
* Merge branch 'main' into akp/CODE-2336
* feat: [CODE-2336]: nit: lint
* feat: [CODE-2336]: nit: rename
* update event and webhook payload
* fix: [CODE-2366]: undo css
* feat: [CODE-2336]: remove service usage
* feat: [CODE-2336]: remove service usage
* Merge remote-tracking branch 'origin/akp/CODE-2336' into akp/CODE-2336

# Conflicts:
#	app/services/webhook/handler_pullreq.go
* feat: [CODE-2336]: remove service usage
* Apply suggestion from code review
* feat: [CODE-2336]: update code to fetch label with values from service
* feat: [CODE-2366]: lint
* feat: [CODE-2366]: new webhook event for label assignment
pull/3576/head
Akhilesh Pandey 2024-10-21 15:13:33 +00:00 committed by Harness
parent 1dbdccfe18
commit c282082cde
8 changed files with 177 additions and 5 deletions

View File

@ -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
}

View File

@ -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...)
}

View File

@ -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

View File

@ -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
})

View File

@ -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"`
}

View File

@ -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,
)
}

View File

@ -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
}

View File

@ -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,
})