mirror of
https://github.com/harness/drone.git
synced 2025-05-05 23:42:57 +00:00
Add comment mentions metadata and return id to principal info mapping in list activity response (#2024)
This commit is contained in:
parent
423801d51a
commit
e31f33adde
@ -47,6 +47,16 @@ func (c *Controller) ActivityList(
|
|||||||
return nil, fmt.Errorf("failed to list pull requests activities: %w", err)
|
return nil, fmt.Errorf("failed to list pull requests activities: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, act := range list {
|
||||||
|
if act.Metadata != nil && act.Metadata.Mentions != nil {
|
||||||
|
mentions, err := c.principalInfoCache.Map(ctx, act.Metadata.Mentions.IDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to fetch activity mentions from principalInfoView: %w", err)
|
||||||
|
}
|
||||||
|
act.Mentions = mentions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
list = removeDeletedComments(list)
|
list = removeDeletedComments(list)
|
||||||
|
|
||||||
return list, nil
|
return list, nil
|
||||||
|
@ -127,6 +127,12 @@ func (c *Controller) CommentCreate(
|
|||||||
// generate all metadata updates
|
// generate all metadata updates
|
||||||
var metadataUpdates []types.PullReqActivityMetadataUpdate
|
var metadataUpdates []types.PullReqActivityMetadataUpdate
|
||||||
|
|
||||||
|
metadataUpdates, principalInfos, err := c.appendMetadataUpdateForMentions(
|
||||||
|
ctx, metadataUpdates, in.Text)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to update metadata for mentions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// suggestion metadata in case of code comments or code comment replies (don't restrict to either side for now).
|
// suggestion metadata in case of code comments or code comment replies (don't restrict to either side for now).
|
||||||
if in.IsCodeComment() || (in.IsReply() && parentAct.IsValidCodeComment()) {
|
if in.IsCodeComment() || (in.IsReply() && parentAct.IsValidCodeComment()) {
|
||||||
metadataUpdates = appendMetadataUpdateForSuggestions(metadataUpdates, in.Text)
|
metadataUpdates = appendMetadataUpdateForSuggestions(metadataUpdates, in.Text)
|
||||||
@ -192,6 +198,9 @@ func (c *Controller) CommentCreate(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Populate activity mentions (used only for response purposes).
|
||||||
|
act.Mentions = principalInfos
|
||||||
|
|
||||||
if in.IsCodeComment() {
|
if in.IsCodeComment() {
|
||||||
// Migrate the comment if necessary... Note: we still need to return the code comment as is.
|
// Migrate the comment if necessary... Note: we still need to return the code comment as is.
|
||||||
c.migrateCodeComment(ctx, repo, pr, in, act.AsCodeComment(), cut)
|
c.migrateCodeComment(ctx, repo, pr, in, act.AsCodeComment(), cut)
|
||||||
@ -428,3 +437,29 @@ func appendMetadataUpdateForSuggestions(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) appendMetadataUpdateForMentions(
|
||||||
|
ctx context.Context,
|
||||||
|
updates []types.PullReqActivityMetadataUpdate,
|
||||||
|
comment string,
|
||||||
|
) ([]types.PullReqActivityMetadataUpdate, map[int64]*types.PrincipalInfo, error) {
|
||||||
|
principalInfos, err := c.processMentions(ctx, comment)
|
||||||
|
if err != nil {
|
||||||
|
return nil, map[int64]*types.PrincipalInfo{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ids := make([]int64, len(principalInfos))
|
||||||
|
i := 0
|
||||||
|
for id := range principalInfos {
|
||||||
|
ids[i] = id
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(
|
||||||
|
updates,
|
||||||
|
types.WithPullReqActivityMentionsMetadataUpdate(
|
||||||
|
func(m *types.PullReqActivityMentionsMetadata) {
|
||||||
|
m.IDs = ids
|
||||||
|
}),
|
||||||
|
), principalInfos, nil
|
||||||
|
}
|
||||||
|
@ -83,6 +83,13 @@ func (c *Controller) CommentUpdate(
|
|||||||
// generate all metadata updates
|
// generate all metadata updates
|
||||||
var metadataUpdates []types.PullReqActivityMetadataUpdate
|
var metadataUpdates []types.PullReqActivityMetadataUpdate
|
||||||
|
|
||||||
|
metadataUpdates, principalInfos, err := c.appendMetadataUpdateForMentions(
|
||||||
|
ctx, metadataUpdates, in.Text,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to update metadata for mentions: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// suggestion metadata in case of code comments or code comment replies (don't restrict to either side for now).
|
// suggestion metadata in case of code comments or code comment replies (don't restrict to either side for now).
|
||||||
if act.IsValidCodeComment() || (act.IsReply() && parentAct.IsValidCodeComment()) {
|
if act.IsValidCodeComment() || (act.IsReply() && parentAct.IsValidCodeComment()) {
|
||||||
metadataUpdates = appendMetadataUpdateForSuggestions(metadataUpdates, in.Text)
|
metadataUpdates = appendMetadataUpdateForSuggestions(metadataUpdates, in.Text)
|
||||||
@ -99,6 +106,9 @@ func (c *Controller) CommentUpdate(
|
|||||||
return nil, fmt.Errorf("failed to update comment: %w", err)
|
return nil, fmt.Errorf("failed to update comment: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Populate activity mentions (used only for response purposes).
|
||||||
|
act.Mentions = principalInfos
|
||||||
|
|
||||||
if err = c.sseStreamer.Publish(ctx, repo.ParentID, enum.SSETypePullRequestUpdated, pr); err != nil {
|
if err = c.sseStreamer.Publish(ctx, repo.ParentID, enum.SSETypePullRequestUpdated, pr); err != nil {
|
||||||
log.Ctx(ctx).Warn().Err(err).Msg("failed to publish PR changed event")
|
log.Ctx(ctx).Warn().Err(err).Msg("failed to publish PR changed event")
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ type Controller struct {
|
|||||||
reviewerStore store.PullReqReviewerStore
|
reviewerStore store.PullReqReviewerStore
|
||||||
repoStore store.RepoStore
|
repoStore store.RepoStore
|
||||||
principalStore store.PrincipalStore
|
principalStore store.PrincipalStore
|
||||||
|
principalInfoCache store.PrincipalInfoCache
|
||||||
fileViewStore store.PullReqFileViewStore
|
fileViewStore store.PullReqFileViewStore
|
||||||
membershipStore store.MembershipStore
|
membershipStore store.MembershipStore
|
||||||
checkStore store.CheckStore
|
checkStore store.CheckStore
|
||||||
@ -74,6 +75,7 @@ func NewController(
|
|||||||
pullreqReviewerStore store.PullReqReviewerStore,
|
pullreqReviewerStore store.PullReqReviewerStore,
|
||||||
repoStore store.RepoStore,
|
repoStore store.RepoStore,
|
||||||
principalStore store.PrincipalStore,
|
principalStore store.PrincipalStore,
|
||||||
|
principalInfoCache store.PrincipalInfoCache,
|
||||||
fileViewStore store.PullReqFileViewStore,
|
fileViewStore store.PullReqFileViewStore,
|
||||||
membershipStore store.MembershipStore,
|
membershipStore store.MembershipStore,
|
||||||
checkStore store.CheckStore,
|
checkStore store.CheckStore,
|
||||||
@ -97,6 +99,7 @@ func NewController(
|
|||||||
reviewerStore: pullreqReviewerStore,
|
reviewerStore: pullreqReviewerStore,
|
||||||
repoStore: repoStore,
|
repoStore: repoStore,
|
||||||
principalStore: principalStore,
|
principalStore: principalStore,
|
||||||
|
principalInfoCache: principalInfoCache,
|
||||||
fileViewStore: fileViewStore,
|
fileViewStore: fileViewStore,
|
||||||
membershipStore: membershipStore,
|
membershipStore: membershipStore,
|
||||||
checkStore: checkStore,
|
checkStore: checkStore,
|
||||||
|
63
app/api/controller/pullreq/mentions.go
Normal file
63
app/api/controller/pullreq/mentions.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// 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 pullreq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/harness/gitness/types"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Controller) processMentions(
|
||||||
|
ctx context.Context,
|
||||||
|
text string,
|
||||||
|
) (map[int64]*types.PrincipalInfo, error) {
|
||||||
|
mentions := parseMentions(ctx, text)
|
||||||
|
if len(mentions) == 0 {
|
||||||
|
return map[int64]*types.PrincipalInfo{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
infos, err := c.principalInfoCache.Map(ctx, mentions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to fetch info from principalInfoCache: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return infos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var mentionRegex = regexp.MustCompile(`@\[(\d+)\]`)
|
||||||
|
|
||||||
|
func parseMentions(ctx context.Context, text string) []int64 {
|
||||||
|
matches := mentionRegex.FindAllStringSubmatch(text, -1)
|
||||||
|
|
||||||
|
var mentions []int64
|
||||||
|
for _, match := range matches {
|
||||||
|
if len(match) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if mention, err := strconv.ParseInt(match[1], 10, 64); err == nil {
|
||||||
|
mentions = append(mentions, mention)
|
||||||
|
} else {
|
||||||
|
log.Ctx(ctx).Warn().Err(err).Msgf("failed to parse mention %q", match[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mentions
|
||||||
|
}
|
@ -40,7 +40,7 @@ func ProvideController(tx dbtx.Transactor, urlProvider url.Provider, authorizer
|
|||||||
pullReqStore store.PullReqStore, pullReqActivityStore store.PullReqActivityStore,
|
pullReqStore store.PullReqStore, pullReqActivityStore store.PullReqActivityStore,
|
||||||
codeCommentsView store.CodeCommentView,
|
codeCommentsView store.CodeCommentView,
|
||||||
pullReqReviewStore store.PullReqReviewStore, pullReqReviewerStore store.PullReqReviewerStore,
|
pullReqReviewStore store.PullReqReviewStore, pullReqReviewerStore store.PullReqReviewerStore,
|
||||||
repoStore store.RepoStore, principalStore store.PrincipalStore,
|
repoStore store.RepoStore, principalStore store.PrincipalStore, principalInfoCache store.PrincipalInfoCache,
|
||||||
fileViewStore store.PullReqFileViewStore, membershipStore store.MembershipStore,
|
fileViewStore store.PullReqFileViewStore, membershipStore store.MembershipStore,
|
||||||
checkStore store.CheckStore,
|
checkStore store.CheckStore,
|
||||||
rpcClient git.Interface, eventReporter *pullreqevents.Reporter, codeCommentMigrator *codecomments.Migrator,
|
rpcClient git.Interface, eventReporter *pullreqevents.Reporter, codeCommentMigrator *codecomments.Migrator,
|
||||||
@ -51,7 +51,7 @@ func ProvideController(tx dbtx.Transactor, urlProvider url.Provider, authorizer
|
|||||||
pullReqStore, pullReqActivityStore,
|
pullReqStore, pullReqActivityStore,
|
||||||
codeCommentsView,
|
codeCommentsView,
|
||||||
pullReqReviewStore, pullReqReviewerStore,
|
pullReqReviewStore, pullReqReviewerStore,
|
||||||
repoStore, principalStore,
|
repoStore, principalStore, principalInfoCache,
|
||||||
fileViewStore, membershipStore,
|
fileViewStore, membershipStore,
|
||||||
checkStore,
|
checkStore,
|
||||||
rpcClient, eventReporter,
|
rpcClient, eventReporter,
|
||||||
|
@ -17,14 +17,10 @@ package notification
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
pullreqevents "github.com/harness/gitness/app/events/pullreq"
|
pullreqevents "github.com/harness/gitness/app/events/pullreq"
|
||||||
"github.com/harness/gitness/events"
|
"github.com/harness/gitness/events"
|
||||||
"github.com/harness/gitness/types"
|
"github.com/harness/gitness/types"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommentPayload struct {
|
type CommentPayload struct {
|
||||||
@ -125,7 +121,7 @@ func (s *Service) processCommentCreatedEvent(
|
|||||||
seen[commenter.ID] = true
|
seen[commenter.ID] = true
|
||||||
|
|
||||||
// process mentions
|
// process mentions
|
||||||
mentions, err = s.processMentions(ctx, activity.Text, seen)
|
mentions, err = s.processMentions(ctx, activity.Metadata, seen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
@ -147,29 +143,27 @@ func (s *Service) processCommentCreatedEvent(
|
|||||||
|
|
||||||
func (s *Service) processMentions(
|
func (s *Service) processMentions(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
text string,
|
metadata *types.PullReqActivityMetadata,
|
||||||
seen map[int64]bool,
|
seen map[int64]bool,
|
||||||
) ([]*types.PrincipalInfo, error) {
|
) ([]*types.PrincipalInfo, error) {
|
||||||
var mentions []*types.PrincipalInfo
|
if metadata == nil || metadata.Mentions == nil {
|
||||||
|
|
||||||
commentMentions := parseMentions(ctx, text)
|
|
||||||
if len(commentMentions) == 0 {
|
|
||||||
return []*types.PrincipalInfo{}, nil
|
return []*types.PrincipalInfo{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var mentionIDs []int64
|
var ids []int64
|
||||||
for _, mentionID := range commentMentions {
|
for _, id := range metadata.Mentions.IDs {
|
||||||
if !seen[mentionID] {
|
if !seen[id] {
|
||||||
mentionIDs = append(mentionIDs, mentionID)
|
ids = append(ids, id)
|
||||||
seen[mentionID] = true
|
seen[id] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(mentionIDs) > 0 {
|
if len(ids) == 0 {
|
||||||
var err error
|
return []*types.PrincipalInfo{}, nil
|
||||||
mentions, err = s.principalInfoView.FindMany(ctx, mentionIDs)
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to fetch thread mentions from principalInfoView: %w", err)
|
mentions, err := s.principalInfoView.FindMany(ctx, ids)
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to fetch thread mentions from principalInfoView: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return mentions, nil
|
return mentions, nil
|
||||||
@ -213,23 +207,3 @@ func (s *Service) processParticipants(
|
|||||||
|
|
||||||
return participants, nil
|
return participants, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var mentionRegex = regexp.MustCompile(`@\[(\d+)\]`)
|
|
||||||
|
|
||||||
func parseMentions(ctx context.Context, text string) []int64 {
|
|
||||||
matches := mentionRegex.FindAllStringSubmatch(text, -1)
|
|
||||||
|
|
||||||
var mentions []int64
|
|
||||||
for _, match := range matches {
|
|
||||||
if len(match) < 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if mention, err := strconv.ParseInt(match[1], 10, 64); err == nil {
|
|
||||||
mentions = append(mentions, mention)
|
|
||||||
} else {
|
|
||||||
log.Ctx(ctx).Warn().Err(err).Msgf("failed to parse mention %q", match[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mentions
|
|
||||||
}
|
|
||||||
|
@ -257,7 +257,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, pullReqFileViewStore, membershipStore, checkStore, gitInterface, eventsReporter, migrator, pullreqService, protectionManager, streamer, codeownersService, lockerLocker)
|
pullreqController := pullreq2.ProvideController(transactor, provider, authorizer, pullReqStore, pullReqActivityStore, codeCommentView, pullReqReviewStore, pullReqReviewerStore, repoStore, principalStore, principalInfoCache, pullReqFileViewStore, membershipStore, checkStore, gitInterface, eventsReporter, migrator, pullreqService, protectionManager, streamer, codeownersService, lockerLocker)
|
||||||
webhookConfig := server.ProvideWebhookConfig(config)
|
webhookConfig := server.ProvideWebhookConfig(config)
|
||||||
webhookStore := database.ProvideWebhookStore(db)
|
webhookStore := database.ProvideWebhookStore(db)
|
||||||
webhookExecutionStore := database.ProvideWebhookExecutionStore(db)
|
webhookExecutionStore := database.ProvideWebhookExecutionStore(db)
|
||||||
|
@ -55,6 +55,8 @@ type PullReqActivity struct {
|
|||||||
Resolver *PrincipalInfo `json:"resolver,omitempty"`
|
Resolver *PrincipalInfo `json:"resolver,omitempty"`
|
||||||
|
|
||||||
CodeComment *CodeCommentFields `json:"code_comment,omitempty"`
|
CodeComment *CodeCommentFields `json:"code_comment,omitempty"`
|
||||||
|
|
||||||
|
Mentions map[int64]*PrincipalInfo `json:"mentions,omitempty"` // used only in response
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *PullReqActivity) IsValidCodeComment() bool {
|
func (a *PullReqActivity) IsValidCodeComment() bool {
|
||||||
|
@ -17,6 +17,7 @@ package types
|
|||||||
// PullReqActivityMetadata contains metadata related to pull request activity.
|
// PullReqActivityMetadata contains metadata related to pull request activity.
|
||||||
type PullReqActivityMetadata struct {
|
type PullReqActivityMetadata struct {
|
||||||
Suggestions *PullReqActivitySuggestionsMetadata `json:"suggestions,omitempty"`
|
Suggestions *PullReqActivitySuggestionsMetadata `json:"suggestions,omitempty"`
|
||||||
|
Mentions *PullReqActivityMentionsMetadata `json:"mentions,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PullReqActivityMetadata) IsEmpty() bool {
|
func (m *PullReqActivityMetadata) IsEmpty() bool {
|
||||||
@ -64,3 +65,28 @@ func WithPullReqActivitySuggestionsMetadataUpdate(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PullReqActivityMentionsMetadata contains metadata for code comment mentions.
|
||||||
|
type PullReqActivityMentionsMetadata struct {
|
||||||
|
IDs []int64 `json:"ids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PullReqActivityMentionsMetadata) IsEmpty() bool {
|
||||||
|
return len(m.IDs) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithPullReqActivityMentionsMetadataUpdate(
|
||||||
|
f func(m *PullReqActivityMentionsMetadata),
|
||||||
|
) PullReqActivityMetadataUpdate {
|
||||||
|
return pullReqActivityMetadataUpdateFunc(func(m *PullReqActivityMetadata) {
|
||||||
|
if m.Mentions == nil {
|
||||||
|
m.Mentions = &PullReqActivityMentionsMetadata{}
|
||||||
|
}
|
||||||
|
|
||||||
|
f(m.Mentions)
|
||||||
|
|
||||||
|
if m.Mentions.IsEmpty() {
|
||||||
|
m.Mentions = nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user