Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gitlab activity callback #3140

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cla-backend-go/cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"strconv"
"strings"

gitlab_activity "github.com/communitybridge/easycla/cla-backend-go/v2/gitlab-activity"

"github.com/go-openapi/strfmt"

"github.com/communitybridge/easycla/cla-backend-go/v2/gitlab_organizations"
Expand Down Expand Up @@ -302,6 +304,7 @@ func server(localMode bool) http.Handler {
v2MetricsService := metrics.NewService(metricsRepo, v1ProjectClaGroupRepo)
githubOrganizationsService := github_organizations.NewService(githubOrganizationsRepo, repositoriesRepo, v1ProjectClaGroupRepo)
gitlabOrganizationsService := gitlab_organizations.NewService(gitlabOrganizationRepo, v1ProjectClaGroupRepo)
gitlabActivityService := gitlab_activity.NewService(gitlabOrganizationRepo, repositoriesRepo, usersRepo, signaturesRepo, v1ProjectClaGroupRepo, v1CompanyRepo, signaturesRepo)
v2GithubOrganizationsService := v2GithubOrganizations.NewService(githubOrganizationsRepo, repositoriesRepo, v1ProjectClaGroupRepo, githubOrganizationsService)
autoEnableService := dynamo_events.NewAutoEnableService(v1RepositoriesService, repositoriesRepo, githubOrganizationsRepo, v1ProjectClaGroupRepo, v1ProjectService)
v2GithubActivityService := v2GithubActivity.NewService(repositoriesRepo, githubOrganizationsRepo, eventsService, autoEnableService, emailService)
Expand Down Expand Up @@ -342,6 +345,7 @@ func server(localMode bool) http.Handler {
github_organizations.Configure(api, githubOrganizationsService, eventsService)
v2GithubOrganizations.Configure(v2API, v2GithubOrganizationsService, eventsService)
gitlab_organizations.Configure(v2API, gitlabOrganizationsService, eventsService)
gitlab_activity.Configure(v2API, gitlabActivityService, eventsService)
repositories.Configure(api, v1RepositoriesService, eventsService)
v2Repositories.Configure(v2API, v2RepositoriesService, eventsService)
gerrits.Configure(api, gerritService, v1ProjectService, eventsService)
Expand Down
162 changes: 162 additions & 0 deletions cla-backend-go/gitlab/mr.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright The Linux Foundation and each contributor to CommunityBridge.
// SPDX-License-Identifier: MIT

package gitlab

import (
"fmt"
"strings"

log "github.com/communitybridge/easycla/cla-backend-go/logging"
"github.com/xanzy/go-gitlab"
)

// FetchMrInfo is responsible for fetching the MR info for given project
func FetchMrInfo(client *gitlab.Client, projectID int, mergeID int) (*gitlab.MergeRequest, error) {
m, _, err := client.MergeRequests.GetMergeRequest(projectID, mergeID, &gitlab.GetMergeRequestsOptions{})
if err != nil {
return nil, fmt.Errorf("fetching merge request : %d for project : %v failed : %v", mergeID, projectID, err)
}

return m, nil
}

// FetchMrParticipants is responsible to get unique mr participants
func FetchMrParticipants(client *gitlab.Client, projectID int, mergeID int, unique bool) ([]*gitlab.User, error) {
commits, _, err := client.MergeRequests.GetMergeRequestCommits(projectID, mergeID, &gitlab.GetMergeRequestCommitsOptions{})
if err != nil {
return nil, fmt.Errorf("fetching gitlab participants for project : %d and merge id : %d, failed : %v", projectID, mergeID, err)
}

if len(commits) == 0 {
return nil, nil
}

var results []*gitlab.User
uniqueUsers := map[int]bool{}

for _, commit := range commits {
authorEmail := commit.AuthorEmail
authorName := commit.AuthorName

log.Debugf("user email found : %s, user name : %s, searching in gitlab ...", authorEmail, authorName)

var user *gitlab.User
if authorName != "" {
user, err = searchForUser(client, authorEmail)
if err != nil {
return nil, fmt.Errorf("searching for author email : %s, failed : %v", authorEmail, err)
}
}

if authorName != "" && user == nil {
user, err = searchForUser(client, authorName)
if err != nil {
return nil, fmt.Errorf("searching for author name : %s, failed : %v", authorName, err)
}
}

if user == nil {
return nil, fmt.Errorf("no users found for commit author email : %s, name : %s", authorEmail, authorName)
}

if uniqueUsers[user.ID] {
continue
}

results = append(results, user)
uniqueUsers[user.ID] = true
}

return results, nil
}

// SetCommitStatus is responsible for setting the MR status for commit sha
func SetCommitStatus(client *gitlab.Client, projectID int, commitSha string, state gitlab.BuildStateValue, message string) error {
options := &gitlab.SetCommitStatusOptions{
State: state,
Name: gitlab.String("easyCLA Bot"),
Description: gitlab.String(message),
}

if state == gitlab.Failed {
options.TargetURL = gitlab.String("http://localhost:8080/gitlab/sign")
}

_, _, err := client.Commits.SetCommitStatus(projectID, commitSha, options)
if err != nil {
return fmt.Errorf("setting commit status for the sha : %s and project id : %d failed : %v", commitSha, projectID, err)
}

return nil
}

// SetMrComment is responsible for setting the comment body for project and merge id
func SetMrComment(client *gitlab.Client, projectID int, mergeID int, state gitlab.BuildStateValue, message string) error {
covered := `<a href="http://localhost:8080">
<img src="https://s3.amazonaws.com/cla-project-logo-dev/cla-signed.svg" alt="covered" align="left" height="28" width="328" ></a><br/>`
failed := `<a href="http://localhost:8080">
<img src="https://s3.amazonaws.com/cla-project-logo-dev/cla-not-signed.svg" alt="covered" align="left" height="28" width="328" ></a><br/>`

var body string
if state == gitlab.Failed {
body = failed
} else {
body = covered
}

notes, _, err := client.Notes.ListMergeRequestNotes(projectID, mergeID, &gitlab.ListMergeRequestNotesOptions{})
if err != nil {
return fmt.Errorf("fetching comments for project id : %d and merge id : %d : failed %v", projectID, mergeID, err)
}

var previousNote *gitlab.Note
if len(notes) > 0 {
for _, n := range notes {
if strings.Contains(n.Body, "cla-signed.svg") || strings.Contains(n.Body, "cla-not-signed.svg") {
previousNote = n
break
}
}
}

if previousNote == nil {
log.Debugf("no previous comments found for project id : %d and merge id : %d", projectID, mergeID)
_, _, err = client.Notes.CreateMergeRequestNote(projectID, mergeID, &gitlab.CreateMergeRequestNoteOptions{
Body: &body,
})
if err != nil {
return fmt.Errorf("creating comment for project id : %d and merge id : %d : failed %v", projectID, mergeID, err)
}
} else {
log.Debugf("previous comments found for project id : %d and merge id : %d", projectID, mergeID)
_, _, err = client.Notes.UpdateMergeRequestNote(projectID, mergeID, previousNote.ID, &gitlab.UpdateMergeRequestNoteOptions{
Body: &body,
})
if err != nil {
return fmt.Errorf("updtae comment for project id : %d and merge id : %d : failed %v", projectID, mergeID, err)
}
}

return nil
}

func searchForUser(client *gitlab.Client, search string) (*gitlab.User, error) {
users, _, err := client.Users.ListUsers(&gitlab.ListUsersOptions{
Search: gitlab.String(search),
})

if err != nil {
return nil, fmt.Errorf("searching for user string : %s failed : %v", search, err)
}

if len(users) == 0 {
return nil, nil
}

if len(users) > 1 {
return nil, fmt.Errorf("found more than one gitlab user for search string : %s", search)
}

return users[0], nil
}
52 changes: 52 additions & 0 deletions cla-backend-go/gitlab/organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,17 @@ package gitlab
import (
"fmt"

log "github.com/communitybridge/easycla/cla-backend-go/logging"

"github.com/xanzy/go-gitlab"
)

// UserGroup represents gitlab group
type UserGroup struct {
Name string
FullPath string
}

// GetGroupByName gets a gitlab Group by the given name
func GetGroupByName(client *gitlab.Client, name string) (*gitlab.Group, error) {
groups, _, err := client.Groups.ListGroups(&gitlab.ListGroupsOptions{})
Expand All @@ -24,3 +32,47 @@ func GetGroupByName(client *gitlab.Client, name string) (*gitlab.Group, error) {

return nil, nil
}

// ListUserProjectGroups fetches the unique groups of a gitlab users groups,
// note: it doesn't list the projects/groups the user is member of ..., it's very limited
func ListUserProjectGroups(client *gitlab.Client, userID int) ([]*UserGroup, error) {
listOptions := &gitlab.ListProjectsOptions{
ListOptions: gitlab.ListOptions{
PerPage: 100,
}}

userGroupsMap := map[string]*UserGroup{}
for {
log.Debugf("fetching projects for user id : %d with options : %v", userID, listOptions.ListOptions)
projects, resp, err := client.Projects.ListUserProjects(userID, listOptions)
if err != nil {
return nil, fmt.Errorf("listing user : %d projects failed : %v", userID, err)
}
log.Printf("fetched %d projects for the user ", len(projects))

if len(projects) == 0 {
break
}

for _, p := range projects {
log.Debugf("checking following project : %s", p.PathWithNamespace)
log.Debugf("fetched following namespace : %+v", p.Namespace)
userGroupsMap[p.Namespace.FullPath] = &UserGroup{
Name: p.Namespace.Name,
FullPath: p.Namespace.FullPath,
}
}

if listOptions.Page >= resp.NextPage {
break
}
listOptions.Page = resp.NextPage
}

var userGroups []*UserGroup
for _, v := range userGroupsMap {
userGroups = append(userGroups, v)
}

return userGroups, nil
}
4 changes: 4 additions & 0 deletions cla-backend-go/gitlab/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright The Linux Foundation and each contributor to CommunityBridge.
// SPDX-License-Identifier: MIT

package gitlab
4 changes: 4 additions & 0 deletions cla-backend-go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
Expand Down Expand Up @@ -652,11 +653,13 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/peterh/liner v1.2.1 h1:O4BlKaq/LWu6VRWmol4ByWfzx6MfXc5Op5HETyIy5yg=
github.com/peterh/liner v1.2.1/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0 h1:wBza4Dlm/NCQF572oSGNZ69flNFxlwIHjtwS6oy3Rvw=
github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1 h1:VasscCm72135zRysgrJDKsntdmPN+OuU3+nnHYA9wyc=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
Expand Down Expand Up @@ -1260,6 +1263,7 @@ honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
Expand Down
5 changes: 5 additions & 0 deletions cla-backend-go/signatures/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -1516,6 +1516,11 @@ func (repo repository) GetProjectCompanyEmployeeSignatures(ctx context.Context,
filter = addAndCondition(filter, expression.Name(SignatureUserGitHubUsername).Equal(expression.Value(criteria.GitHubUsername)), &filterAdded)
}

if criteria != nil && criteria.GitHubUsername != "" {
log.WithFields(f).Debugf("adding Gitlabusername criteria filter for :%s ", criteria.GitlabUsername)
filter = addAndCondition(filter, expression.Name(SignatureUserGitlabUsername).Equal(expression.Value(criteria.GitlabUsername)), &filterAdded)
}

if criteria != nil && criteria.UserEmail != "" {
log.WithFields(f).Debugf("adding useremail criteria filter for : %s ", criteria.UserEmail)
filter = addAndCondition(filter, expression.Name("user_email").Equal(expression.Value(criteria.UserEmail)), &filterAdded)
Expand Down
38 changes: 37 additions & 1 deletion cla-backend-go/swagger/cla.v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3820,7 +3820,7 @@ paths:
get:
summary: The endpoint is called after user authorizes EasyCLA bot
description: The endpoint is responsible for storing the access token for the user and registering the webhooks is autoenable is on
security: []
security: [ ]
operationId: gitlabOauthCallback
parameters:
- name: code
Expand Down Expand Up @@ -3853,6 +3853,35 @@ paths:
tags:
- gitlab-activity

/gitlab/activity:
post:
summary: Gitlab Activity Callback Handler
description: Gitlab Activity Callback Handler reacts to Gitlab events emmited.
security: [ ]
operationId: gitlabActivity
parameters:
- $ref: "#/parameters/x-request-id"
- $ref: "#/parameters/x-github-event"
- $ref: "#/parameters/x-hub-signature"
- name: gitlabActivityInput
in: body
schema:
$ref: '#/definitions/gitlab-activity-input'
responses:
'200':
description: 'Success'
'400':
$ref: '#/responses/invalid-request'
'401':
$ref: '#/responses/unauthorized'
'403':
$ref: '#/responses/forbidden'
'500':
$ref: '#/responses/internal-server-error'
tags:
- gitlab-activity


responses:
unauthorized:
description: Unauthorized
Expand Down Expand Up @@ -4204,6 +4233,13 @@ definitions:
type: string
additionalProperties: true

gitlab-activity-input:
type: object
properties:
object_kind:
type: string
additionalProperties: true

github-repository-input:
type: object
required:
Expand Down
Loading