From ab72ca91853b2c2d772f24ba1b2f820ee3c46f49 Mon Sep 17 00:00:00 2001 From: Akhilesh Pandey Date: Thu, 14 Nov 2024 22:48:04 +0000 Subject: [PATCH] feat: [CODE-2472]: CODE Comment updated webhook (#2850) * feat: [CODE-2472]: review * feat: [CODE-2742]: review changes * feat: [CODE-2742]: cast extractor code return * feat: [CODE-2742]: wire changes * feat: [CODE-2742]: wire changes * Merge branch 'main' into akp/CODE-2472 * Apply suggestion from code review * review changes * Apply suggestions from code review * feat: [CODE-2472]: remove commit info * feat: [CODE-2472]: remove json tags * feat: [CODE-2472]: wire changes * feat: [CODE-2472]: fix minor issues * Merge branch 'main' into akp/CODE-2472 * feat: [CODE-2472]: Update the timestamps * feat: [CODE-2472]: Pullreq comment updated changes * feat: [CODE-2472]: changes * feat: [CODE-2472]: update name to edited * feat: [CODE-2472]: update name to edited * Merge branch 'main' into akp/CODE-2472 * feat: [CODE-2472]: status change comment changes * feat: [CODE-2472]: Text changes only * feat: [CODE-2472]: initial code --- app/api/controller/pullreq/comment_update.go | 23 +++++ .../pullreq/events_code_comment_updated.go | 57 ++++++++++++ app/services/webhook/handler_pullreq.go | 87 +++++++++++++++++-- app/services/webhook/service.go | 4 + app/services/webhook/types.go | 20 +++++ app/services/webhook/wire.go | 2 + cmd/gitness/wire_gen.go | 2 +- types/enum/webhook.go | 6 ++ 8 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 app/events/pullreq/events_code_comment_updated.go diff --git a/app/api/controller/pullreq/comment_update.go b/app/api/controller/pullreq/comment_update.go index 8ebfd6db6..487ef98fd 100644 --- a/app/api/controller/pullreq/comment_update.go +++ b/app/api/controller/pullreq/comment_update.go @@ -21,6 +21,7 @@ import ( "time" "github.com/harness/gitness/app/auth" + events "github.com/harness/gitness/app/events/pullreq" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" @@ -119,5 +120,27 @@ func (c *Controller) CommentUpdate( log.Ctx(ctx).Warn().Err(err).Msg("failed to publish PR changed event") } + c.reportCommentUpdated(ctx, pr, session.Principal.ID, act.ID, act.IsReply()) + return act, nil } + +func (c *Controller) reportCommentUpdated( + ctx context.Context, + pr *types.PullReq, + principalID int64, + actID int64, + isReply bool, +) { + c.eventReporter.CommentUpdated(ctx, &events.CommentUpdatedPayload{ + Base: events.Base{ + PullReqID: pr.ID, + SourceRepoID: pr.SourceRepoID, + TargetRepoID: pr.TargetRepoID, + PrincipalID: principalID, + Number: pr.Number, + }, + ActivityID: actID, + IsReply: isReply, + }) +} diff --git a/app/events/pullreq/events_code_comment_updated.go b/app/events/pullreq/events_code_comment_updated.go new file mode 100644 index 000000000..43a1123c7 --- /dev/null +++ b/app/events/pullreq/events_code_comment_updated.go @@ -0,0 +1,57 @@ +// 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 CommentUpdatedEvent events.EventType = "comment-updated" + +type CommentUpdatedPayload struct { + Base + ActivityID int64 `json:"activity_id"` + IsReply bool `json:"is_reply"` +} + +func (r *Reporter) CommentUpdated( + ctx context.Context, + payload *CommentUpdatedPayload, +) { + if payload == nil { + return + } + + eventID, err := events.ReporterSendEvent(r.innerReporter, ctx, CommentUpdatedEvent, payload) + if err != nil { + log.Ctx(ctx).Err(err).Msgf( + "failed to send pull request comment updated event for event id '%s'", eventID, + ) + return + } + + log.Ctx(ctx).Debug().Msgf("reported pull request comment updated event with id '%s'", eventID) +} + +func (r *Reader) RegisterCommentUpdated( + fn events.HandlerFunc[*CommentUpdatedPayload], + opts ...events.HandlerOption, +) error { + return events.ReaderRegisterEvent(r.innerReader, CommentUpdatedEvent, fn, opts...) +} diff --git a/app/services/webhook/handler_pullreq.go b/app/services/webhook/handler_pullreq.go index 95a97a31a..878f761b6 100644 --- a/app/services/webhook/handler_pullreq.go +++ b/app/services/webhook/handler_pullreq.go @@ -360,12 +360,89 @@ func (s *Service) handleEventPullReqComment( ID: activity.ID, ParentID: activity.ParentID, Kind: activity.Kind, + Created: activity.Created, + Updated: activity.Updated, }, }, }, nil }) } +// PullReqCommentUpdatedPayload describes the body of the pullreq comment create trigger. +type PullReqCommentUpdatedPayload struct { + BaseSegment + PullReqSegment + PullReqTargetReferenceSegment + ReferenceSegment + PullReqCommentSegment +} + +// handleEventPullReqCommentUpdated handles updated events for pull request comments. +func (s *Service) handleEventPullReqCommentUpdated( + ctx context.Context, + event *events.Event[*pullreqevents.CommentUpdatedPayload], +) error { + return s.triggerForEventWithPullReq( + ctx, + enum.WebhookTriggerPullReqCommentUpdated, + event.ID, + event.Payload.PrincipalID, + event.Payload.PullReqID, + func(principal *types.Principal, pr *types.PullReq, targetRepo, sourceRepo *types.Repository) (any, error) { + targetRepoInfo := repositoryInfoFrom(ctx, targetRepo, s.urlProvider) + 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 &PullReqCommentUpdatedPayload{ + BaseSegment: BaseSegment{ + Trigger: enum.WebhookTriggerPullReqCommentUpdated, + Repo: targetRepoInfo, + Principal: principalInfoFrom(principal.ToPrincipalInfo()), + }, + PullReqSegment: PullReqSegment{ + PullReq: pullReqInfoFrom(ctx, pr, targetRepo, s.urlProvider), + }, + PullReqTargetReferenceSegment: PullReqTargetReferenceSegment{ + TargetRef: ReferenceInfo{ + Name: gitReferenceNamePrefixBranch + pr.TargetBranch, + Repo: targetRepoInfo, + }, + }, + ReferenceSegment: ReferenceSegment{ + Ref: ReferenceInfo{ + Name: gitReferenceNamePrefixBranch + pr.SourceBranch, + Repo: sourceRepoInfo, + }, + }, + PullReqCommentSegment: PullReqCommentSegment{ + CommentInfo: CommentInfo{ + Text: activity.Text, + ID: activity.ID, + ParentID: activity.ParentID, + Created: activity.Created, + Updated: activity.Updated, + }, + CodeCommentInfo: extractCodeCommentInfoIfAvailable(activity), + }, + }, nil + }) +} + +func extractCodeCommentInfoIfAvailable( + activity *types.PullReqActivity, +) *CodeCommentInfo { + if !activity.IsValidCodeComment() { + return nil + } + return (*CodeCommentInfo)(activity.CodeComment) +} + // PullReqLabelAssignedPayload describes the body of the pullreq label assignment trigger. type PullReqLabelAssignedPayload struct { BaseSegment @@ -393,19 +470,19 @@ func (s *Service) handleEventPullReqLabelAssigned( return nil, fmt.Errorf("failed to find label by id: %w", err) } - var labelValueKey *string + var labelValue *string if event.Payload.ValueID != nil { - value, err := s.labelStore.FindByID(ctx, *event.Payload.ValueID) + value, err := s.labelValueStore.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 + labelValue = &value.Value } targetRepoInfo := repositoryInfoFrom(ctx, targetRepo, s.urlProvider) return &PullReqLabelAssignedPayload{ BaseSegment: BaseSegment{ - Trigger: enum.WebhookTriggerPullReqCommentCreated, + Trigger: enum.WebhookTriggerPullReqLabelAssigned, Repo: targetRepoInfo, Principal: principalInfoFrom(principal.ToPrincipalInfo()), }, @@ -417,7 +494,7 @@ func (s *Service) handleEventPullReqLabelAssigned( ID: event.Payload.LabelID, Key: label.Key, ValueID: event.Payload.ValueID, - Value: labelValueKey, + Value: labelValue, }, }, }, nil diff --git a/app/services/webhook/service.go b/app/services/webhook/service.go index 7eece64ee..d98f66411 100644 --- a/app/services/webhook/service.go +++ b/app/services/webhook/service.go @@ -92,6 +92,7 @@ type Service struct { git git.Interface activityStore store.PullReqActivityStore labelStore store.LabelStore + labelValueStore store.LabelValueStore encrypter encrypt.Encrypter secureHTTPClient *http.Client @@ -122,6 +123,7 @@ func NewService( encrypter encrypt.Encrypter, labelStore store.LabelStore, webhookURLProvider URLProvider, + labelValueStore store.LabelValueStore, ) (*Service, error) { if err := config.Prepare(); err != nil { return nil, fmt.Errorf("provided webhook service config is invalid: %w", err) @@ -148,6 +150,7 @@ func NewService( config: config, labelStore: labelStore, + labelValueStore: labelValueStore, webhookURLProvider: webhookURLProvider, } @@ -192,6 +195,7 @@ func NewService( _ = r.RegisterBranchUpdated(service.handleEventPullReqBranchUpdated) _ = r.RegisterClosed(service.handleEventPullReqClosed) _ = r.RegisterCommentCreated(service.handleEventPullReqComment) + _ = r.RegisterCommentUpdated(service.handleEventPullReqCommentUpdated) _ = r.RegisterMerged(service.handleEventPullReqMerged) _ = r.RegisterUpdated(service.handleEventPullReqUpdated) _ = r.RegisterLabelAssigned(service.handleEventPullReqLabelAssigned) diff --git a/app/services/webhook/types.go b/app/services/webhook/types.go index c3264b0b6..ff6a32db8 100644 --- a/app/services/webhook/types.go +++ b/app/services/webhook/types.go @@ -78,6 +78,13 @@ type PullReqSegment struct { // PullReqCommentSegment contains details for all pull req comment related payloads for webhooks. type PullReqCommentSegment struct { CommentInfo CommentInfo `json:"comment"` + *CodeCommentInfo +} + +// PullReqCommentUpdatedSegment contains details for pullreq text comment edited payloads for webhooks. +type PullReqCommentUpdatedSegment struct { + CommentInfo + *CodeCommentInfo } // PullReqLabelSegment contains details for all pull req label related payloads for webhooks. @@ -302,6 +309,8 @@ type CommentInfo struct { ID int64 `json:"id"` ParentID *int64 `json:"parent_id,omitempty"` Text string `json:"text"` + Created int64 `json:"created"` + Updated int64 `json:"updated"` Kind enum.PullReqActivityKind `json:"kind"` } @@ -311,3 +320,14 @@ type LabelInfo struct { ValueID *int64 `json:"value_id,omitempty"` Value *string `json:"value,omitempty"` } + +type CodeCommentInfo struct { + Outdated bool `json:"outdated"` + MergeBaseSHA string `json:"merge_base_sha"` + SourceSHA string `json:"source_sha"` + Path string `json:"path"` + LineNew int `json:"line_new"` + SpanNew int `json:"span_new"` + LineOld int `json:"line_old"` + SpanOld int `json:"span_old"` +} diff --git a/app/services/webhook/wire.go b/app/services/webhook/wire.go index fe6ebd60c..c0d77ddc7 100644 --- a/app/services/webhook/wire.go +++ b/app/services/webhook/wire.go @@ -53,6 +53,7 @@ func ProvideService( encrypter encrypt.Encrypter, labelStore store.LabelStore, webhookURLProvider URLProvider, + labelValueStore store.LabelValueStore, ) (*Service, error) { return NewService( ctx, @@ -71,6 +72,7 @@ func ProvideService( encrypter, labelStore, webhookURLProvider, + labelValueStore, ) } diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index a9f151bc0..54b8f3cff 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -366,7 +366,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro webhookStore := database.ProvideWebhookStore(db) webhookExecutionStore := database.ProvideWebhookExecutionStore(db) urlProvider := webhook.ProvideURLProvider(ctx) - webhookService, err := webhook.ProvideService(ctx, webhookConfig, transactor, readerFactory, eventsReaderFactory, webhookStore, webhookExecutionStore, spaceStore, repoStore, pullReqStore, pullReqActivityStore, provider, principalStore, gitInterface, encrypter, labelStore, urlProvider) + webhookService, err := webhook.ProvideService(ctx, webhookConfig, transactor, readerFactory, eventsReaderFactory, webhookStore, webhookExecutionStore, spaceStore, repoStore, pullReqStore, pullReqActivityStore, provider, principalStore, gitInterface, encrypter, labelStore, urlProvider, labelValueStore) if err != nil { return nil, err } diff --git a/types/enum/webhook.go b/types/enum/webhook.go index 370d9fe35..0c01d2775 100644 --- a/types/enum/webhook.go +++ b/types/enum/webhook.go @@ -156,6 +156,10 @@ const ( WebhookTriggerPullReqClosed WebhookTrigger = "pullreq_closed" // WebhookTriggerPullReqCommentCreated gets triggered when a pull request comment gets created. WebhookTriggerPullReqCommentCreated WebhookTrigger = "pullreq_comment_created" + // WebhookTriggerPullReqCommentUpdated gets triggered when a pull request comment gets edited. + WebhookTriggerPullReqCommentUpdated WebhookTrigger = "pullreq_comment_updated" + // WebhookTriggerPullReqCommentStatusUpdated gets triggered when a pull request comment status gets updated. + WebhookTriggerPullReqCommentStatusUpdated WebhookTrigger = "pullreq_comment_status_updated" // WebhookTriggerPullReqMerged gets triggered when a pull request is merged. WebhookTriggerPullReqMerged WebhookTrigger = "pullreq_merged" // WebhookTriggerPullReqUpdated gets triggered when a pull request gets updated. @@ -180,6 +184,8 @@ var webhookTriggers = sortEnum([]WebhookTrigger{ WebhookTriggerPullReqBranchUpdated, WebhookTriggerPullReqClosed, WebhookTriggerPullReqCommentCreated, + WebhookTriggerPullReqCommentUpdated, + WebhookTriggerPullReqCommentStatusUpdated, WebhookTriggerPullReqMerged, WebhookTriggerPullReqLabelAssigned, })